Created
May 9, 2018 12:55
-
-
Save va2577/3323ca6afc2c9e6ca64663ded2246dc0 to your computer and use it in GitHub Desktop.
二つのパターン(ランダム、期待値)の口座残高の推移
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
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8"> | |
<title>simulation</title> | |
<style> | |
input#simulate { | |
width: 100%; | |
} | |
input.error { | |
background-color: red; | |
} | |
span#message { | |
color: red; | |
} | |
</style> | |
</head> | |
<body> | |
<div> | |
<form> | |
<table> | |
<tr> | |
<th>定率</th> | |
<td><input type="text" id="fr" placeholder="0.02" value="0.02"></td> | |
</tr> | |
<tr> | |
<th>口座残高</th> | |
<td><input type="text" id="ab" placeholder="1000000" value="1000000"></td> | |
</tr> | |
<tr> | |
<th>勝率</th> | |
<td><input type="text" id="wr" placeholder="0.30" value="0.30"></td> | |
</tr> | |
<tr> | |
<th>平均利益</th> | |
<td><input type="text" id="ap" placeholder="3" value="3"></td> | |
</tr> | |
<tr> | |
<th>平均損失</th> | |
<td><input type="text" id="al" placeholder="1" value="1"></td> | |
</tr> | |
<tr> | |
<th>機会</th> | |
<td><input type="text" id="o" placeholder="10" value="10"></td> | |
</tr> | |
<tr> | |
<th>試行回数</th> | |
<td><input type="text" id="t" placeholder="100" value="100"></td> | |
</tr> | |
<tr> | |
<td colspan="2"><input type="button" id="simulate" value="シミュレート"></td> | |
</tr> | |
</table> | |
</form> | |
<span id="message"></span> | |
</div> | |
<div height="320" width="640"> | |
<canvas id="canvas"></canvas> | |
</div> | |
<div> | |
<table> | |
<tr> | |
<th>口座残高</th> | |
<td><span id="rab">0</span></td> | |
<td><span id="rab2">0</span></td> | |
</tr> | |
<tr> | |
<th>勝率</th> | |
<td><span id="rwr">0</span></td> | |
<td><span id="rwr2">0</span></td> | |
</tr> | |
<tr> | |
<th>期待値</th> | |
<td><span id="rev">0</span></td> | |
<td><span id="rev2">0</span></td> | |
</tr> | |
<tr> | |
<th>平均月利</th> | |
<td><span id="rm">0</span></td> | |
<td><span id="rm2">0</span></td> | |
</tr> | |
<tr> | |
<th>平均年利</th> | |
<td><span id="ra">0</span></td> | |
<td><span id="ra2">0</span></td> | |
</tr> | |
</table> | |
</div> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.bundle.min.js"></script> | |
<script> | |
const local = { | |
chart: null, | |
comma: number => number.toLocaleString(), | |
round: (number, precision) => { | |
const factor = Math.pow(10, precision); | |
return Math.round(number * factor) / factor; | |
}, | |
last: array => array[array.length - 1], | |
sum: array => array.reduce((p, cv) => p + cv, 0), | |
average: array => local.sum(array) / array.length, | |
validate: ids => { | |
document.getElementById('message').textContent = ''; | |
const entered = id => { | |
const e = document.getElementById(id); | |
const isValid = !(e.value.length === 0 || isNaN(e.value)); | |
e.className = isValid ? '' : 'error'; | |
return isValid; | |
}; | |
if (!ids.every(entered)) { | |
document.getElementById('message').textContent = 'パラメーターを数字で入力してください。'; | |
return false; | |
} | |
if (Number(document.getElementById('t').value) > 2000) { | |
document.getElementById('message').textContent = '試行回数は 2000 以内で入力してください。'; | |
return false; | |
} | |
return true; | |
}, | |
parameters: ids => { | |
return ids.reduce((p, cv) => { | |
p[cv] = Number(document.getElementById(cv).value); | |
return p; | |
}, {}); | |
}, | |
w: wr => Math.random() < wr, | |
rrr: (ap, al) => ap / al, | |
pl: (ab, fr, w, rrr) => Math.floor(ab * fr * (w ? rrr : -1)), | |
ab: (ab, fr, w, rrr) => ab + local.pl(ab, fr, w, rrr), | |
ab2: (ab, fr, ev, i) => Math.floor(ab * Math.pow(1 + fr * ev, i)), | |
wr: array => array.filter(v => v).length / array.length, | |
ev: (wr, rrr) => (wr * rrr / 1) - (1 - wr * (1 / 1)), | |
pr: (ab, o, monthly, annual) => { | |
const times = monthly ? o : annual ? o * 12 : 0; | |
const p = [...Array(Math.ceil(ab.length / times)).keys()] | |
.map(v => ab.filter((v2, i2) => v * times <= i2 && i2 <= (v + 1) * times)) | |
.filter(v => v.length > 1) | |
.map(v => (local.last(v) - v[0]) / v[0]); | |
return local.average(p); | |
}, | |
simulate: (parameters) => { | |
const result = {}; | |
const w = [...Array(parameters.t).keys()].map(() => local.w(parameters.wr)); | |
const rrr = local.rrr(parameters.ap, parameters.al); | |
result.ab = w.reduce((p, cv) => { | |
p.push(local.ab(local.last(p), parameters.fr, cv, rrr)); | |
return p; | |
}, Array.of(parameters.ab)); | |
result.wr = local.wr(w); | |
result.ev = local.ev(result.wr, rrr); | |
result.monthly = local.pr(result.ab, parameters.o, true, false); | |
result.annual = local.pr(result.ab, parameters.o, false, true); | |
return result; | |
}, | |
simulate2: (parameters) => { | |
const result = {}; | |
const rrr = local.rrr(parameters.ap, parameters.al); | |
result.wr = parameters.wr; | |
result.ev = local.ev(parameters.wr, rrr); | |
result.ab = [...Array(parameters.t + 1).keys()] | |
.map((v, i, a) => local.ab2(parameters.ab, parameters.fr, result.ev, i)); | |
result.monthly = local.pr(result.ab, parameters.o, true, false); | |
result.annual = local.pr(result.ab, parameters.o, false, true); | |
return result; | |
}, | |
plot: () => { | |
const ids = ['fr', 'ab', 'wr', 'ap', 'al', 'o', 't']; | |
if (!local.validate(ids)) return; | |
const parameters = local.parameters(ids); | |
const result = local.simulate(parameters); | |
if (!result.hasOwnProperty('ab')) return; | |
local.chart.data.datasets[0].data = result.ab; | |
local.chart.data.labels = result.ab.map((v, i) => i); | |
local.chart.update(); | |
document.getElementById('rab').textContent = local.last(result.ab).toLocaleString(); | |
document.getElementById('rwr').textContent = local.round(result.wr, 2); | |
document.getElementById('rev').textContent = local.round(result.ev, 2); | |
document.getElementById('rm').textContent = local.round(result.monthly, 2).toLocaleString(); | |
document.getElementById('ra').textContent = local.round(result.annual, 2).toLocaleString(); | |
const result2 = local.simulate2(parameters); | |
if (!result2.hasOwnProperty('ab')) return; | |
local.chart.data.datasets[1].data = result2.ab; | |
local.chart.data.labels = result2.ab.map((v, i) => i); | |
local.chart.update(); | |
document.getElementById('rab2').textContent = local.last(result2.ab).toLocaleString(); | |
document.getElementById('rwr2').textContent = local.round(result2.wr, 2); | |
document.getElementById('rev2').textContent = local.round(result2.ev, 2); | |
document.getElementById('rm2').textContent = local.round(result2.monthly, 2).toLocaleString(); | |
document.getElementById('ra2').textContent = local.round(result2.annual, 2).toLocaleString(); | |
} | |
}; | |
document.getElementById('simulate').addEventListener('click', () => { | |
local.plot(); | |
}); | |
window.addEventListener('load', () => { | |
const ctx = document.getElementById('canvas').getContext('2d'); | |
const chartColors = { | |
red: 'rgb(255, 99, 132)', | |
orange: 'rgb(255, 159, 64)', | |
yellow: 'rgb(255, 205, 86)', | |
green: 'rgb(75, 192, 192)', | |
blue: 'rgb(54, 162, 235)', | |
purple: 'rgb(153, 102, 255)', | |
grey: 'rgb(201, 203, 207)' | |
}; | |
const data = { | |
datasets: [ | |
{ | |
backgroundColor: chartColors.red, | |
borderColor: chartColors.red, | |
data: [], | |
fill: false, | |
label: 'ランダム' | |
}, | |
{ | |
backgroundColor: chartColors.blue, | |
borderColor: chartColors.blue, | |
data: [], | |
fill: false, | |
label: '期待値' | |
} | |
], | |
labels: [] | |
}; | |
const options = { | |
scales: { | |
yAxes: [{ | |
ticks: { | |
userCallback: local.comma | |
} | |
}] | |
}, | |
tooltips: { | |
callbacks: { | |
label: (tooltipItem, data) => data.datasets[tooltipItem.datasetIndex].label + ': ' + local.comma(tooltipItem.yLabel) | |
} | |
} | |
}; | |
local.chart = new Chart(ctx, { | |
data: data, | |
options: options, | |
type: 'line' | |
}); | |
local.plot(); | |
}); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment