Created
May 8, 2018 07:33
-
-
Save va2577/af6d755efbe325e34b162756b960e9a7 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="rb">0</span></td> | |
</tr> | |
<tr> | |
<th>勝率</th> | |
<td><span id="rwr">0</span></td> | |
</tr> | |
<tr> | |
<th>期待値</th> | |
<td><span id="rev">0</span></td> | |
</tr> | |
<tr> | |
<th>平均月利</th> | |
<td><span id="rm">0</span></td> | |
</tr> | |
<tr> | |
<th>平均年利</th> | |
<td><span id="ra">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: v => v.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 = (c) => { | |
const e = document.getElementById(c); | |
const v = e.value; | |
const isValid = !(v.length === 0 || isNaN(v)); | |
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((a, c) => { | |
a[c] = Number(document.getElementById(c).value); | |
return a; | |
}, {}); | |
}, | |
rrr: (ap, al) => ap / al, | |
b: (b, fr, ev, i) => Math.floor(b * Math.pow(1 + fr * ev, i)), | |
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: function (parameters) { | |
const result = {}; | |
result.rrr = local.rrr(parameters.ap, parameters.al); | |
result.ev = local.ev(parameters.wr, result.rrr); | |
result.ab = [...Array(parameters.t + 1).keys()] | |
.map((v, i, a) => local.b(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('rb').textContent = local.last(result.ab).toLocaleString(); | |
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(); | |
} | |
}; | |
document.getElementById('simulate').addEventListener('click', () => { | |
local.plot(); | |
}); | |
window.addEventListener('load', () => { | |
const ctx = document.getElementById('canvas').getContext('2d'); | |
const data = { | |
datasets: [{ | |
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