<html><head><meta charset="utf-8"/></head><body><script>
// webGL z-axis 稱為 depth, 以螢幕當基準點 0, 螢幕後面是負值, 前面是正, 螢幕左右是 x 軸, 上下是 y 軸, 螢幕中心是 (0, 0, 0)
// webGL 用的座標稱為卷座標 (clip coordinate), 座標值 x, y, z 都介於 -1 與 1 之間的浮點數
// 物件以原點(0,0,0)當參考點, 稱為世界座標 (world coordinate)
// Vertices 是 3D 物件的頂點座標
// 世界座標可以經由矩陣轉換成 webGL 的卷座標: clip_coordinate = Pmatrix * Vmatix * Mmatrix * world_coordinate
// Mmatrix 是物體移動的矩陣,讓物件可以在世界中自由變換位置甚至旋轉及放大(Translate, scale, rotate)
// Vmatrix 可以當成觀景矩陣,如果將相機觀景點當成原點,以相對觀點來看,等同將世界座標的零點座標(0,0,0)逆轉換(invere transform)也就是移動世界
// Pmatrix 是投射矩陣, 將世界座標最後投射到 GL 用的 -1 到 1 之間的繪圖座標點, 同時可以限定在 canvas 的畫布之內
// vertex and index buffer 用於描述模型的幾何結構, mesh 稱為網目(面), 一般會用三角形的碎形面(fragment)建構物體的表面
// ESSL : Embedded System Shader Language 內嵌系統渲染程式語言
// GLSL : OpenGL ES Shader Language 必須包括點及碎面的渲染程式(Vertex and Fragment shader program)
// Shader Language 用來定義 vertices, transformations, materials, lights, camera 的交互作用下產生的一幅影像
// vertex Shader 點渲染作用於 3D 座標點及線上 , 碎面渲染(Fragment Shader)作用於充實物件表面
// drawElements(),drawArrays() 單元或陣列繪畫
// 參考資料:
// 1. https://webglfundamentals.org/webgl/lessons/webgl-drawing-multiple-things.html
// 2. https://www.tutorialspoint.com/webgl/
// 3. https://www.khronos.org/registry/OpenGL/specs/gl/GLSLangSpec.1.10.pdf
// Init time:
// 1. create shader program, lookup attribute and uniform location
// 2. create buufers and upload data
// 3. create textures and upload data
// Rander time:
// 1. useProgram
// 2. setup attribute and uniform
// 3. drawArrays or drawElements
// GLSL 所有變數使用前必須先宣告資料型態, 基本資料型態:
// void, float, bool, int, vect2, vect3, vect4, bvect2, bvect3, bvect4, ivect2, ivect3, ivect4, mat2, mat3, mat4, sampler1D, sampler2D, sampler3D, samplerCube, sampler1Dshadow, sampler2Dshadow
// void 是忽視的資料, float 是浮點數資料, bool 是布林數資料, int 是整數資料 (V FBI)
// float 可以使用 E 或 e 在科學記號表示式中作為 10 的指數(10^), 句點分離整數與小數
// bool 僅可以用 true, false 賦值
// int 整數前置可以用 0 代表 8 進制整數, 用 0x 或 0X 代表 16 進制整數, 否則一般就是 10 進制整數
// vect 是浮點數的向量, bvect 是布林數的向量, ivect 是整數值的向量
// 方陣是以行為主(column-major order),也就是向量陣列的方陣, mat2 是 2x2 方陣, mat3 是 3x3 方陣, mat4 是 4x4 方陣
// 資料整合可以用 struct newType { } 群聚起來成為新的資料型態(newType), 往後資料宣告時就可加以利用
// 資料型態修飾詞(Type Qualifiers) 可以用 const, attribute, uniform, varying, in, out, inout 關鍵字來修飾變數
// 在函式外部宣告的變數, 它是整體區域(Global scope)變數, 僅可以加上 const, attribute, uniform, varying 其中之一的修飾詞
// 函式輸出入參數只能用 in, out, inout, const 其中之一的修飾詞
// const 顧名思義就是不能修改的常數
// attribute 修飾詞用於將變數當成參數傳遞(像是將 vertices 從 OpenGL 一一傳給 vertext shader)時來使用, 對 vertex shader 而言它是常數
// attirbute 只可以用來修飾 float, vect, mat 等浮點數, 它不能用於修飾陣列(array)或是結構(struct)的資料型態.
// uniform 作用於整體區域的宣告, 基本上它是唯讀的, 可以直接透過 API 或是間接經由 OpenGL 初始化.
// varying 提供 vertex shadow 與 fragement shadow 及固定函式之間作為資料交換介面使用
const π = Math.PI;
let tan = Math.tan;
let cos = Math.cos;
let sin = Math.sin;
class matriX4 { // 4x4 方陣
constructor() { this.m = [1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1]; }
translate(x,y,z){ // bind this with objet I4
this.m[12] += x;
this.m[13] += y;
this.m[14] += z;
return this;
}
scale(sx,sy,sz) { // scale depend on sx,sy,sz)
this.m[0] *= sx;
this.m[5] *= sy;
this.m[10]*= sz;
return this;
}
translatex(s) { this.m[12] += s; return this;}
translatey(s) { this.m[13] += s; return this;}
translatez(s) { this.m[14] += s; return this;}
scalex(s) { this.m[0] *= s; return this;}
scaley(s) { this.m[5] *= s; return this;}
scalez(s) { this.m[10] *= s; return this;}
rotatex(θ) {
let c = cos(θ);
let s = sin(θ);
let m = this.m;
let mv1 = m[1], mv5 = m[5], mv9 = m[9];
m[1] = m[1]*c - m[2]*s;
m[5] = m[5]*c - m[6]*s;
m[9] = m[9]*c - m[10]*s;
m[2] = m[2]*c + mv1*s;
m[6] = m[6]*c + mv5*s;
m[10] = m[10]*c+ mv9*s;
return this;
}
rotatey(θ) {
let c = cos(θ);
let s = sin(θ);
let m = this.m;
let mv0 = m[0], mv4 = m[4], mv8 = m[8];
m[0] = c*m[0] + s*m[2];
m[4] = c*m[4] + s*m[6];
m[8] = c*m[8] + s*m[10];
m[2] = c*m[2] - s*mv0;
m[6] = c*m[6] - s*mv4;
m[10] = c*m[10]- s*mv8;
return this;
}
rotatez(θ) {
let c = cos(θ);
let s = sin(θ);
let m = this.m;
let mv0 = m[0], mv4 = m[4], mv8 = m[8];
m[0] = c*m[0] - s*m[1];
m[4] = c*m[4] - s*m[5];
m[8] = c*m[8] - s*m[9];
m[1] = c*m[1] + s*mv0 ;
m[5] = c*m[5] + s*mv4 ;
m[9] = c*m[9] + s*mv8 ;
return this;
}
projector(θ, near, far, width, height) {
if (near < 1e-4) near = 1e-4;
let depth = far - near;
let factor = tan((π-θ)/2); // 0< θ < π
this.width = width;
this.height= height;
this.m[0] = factor*height/width;
this.m[5] = factor;
this.m[10] = -(near+far)/depth;// width > height
this.m[11] = -1;
this.m[14] = -2*near*far/depth;
this.m[15] = 0 ;
return this;
}
}
class canvas3D{
constructor(width,height,depth) {
let div = document.createElement("div");
let canvas = document.createElement("canvas");
this.context = canvas.getContext('webgl');
this.border = 8;
document.body.style= "background-color: #080808;";
canvas.style.border = this.border+"px solid";
document.body.appendChild(div);
div.align="center";
div.appendChild(canvas);
canvas.width = width;
canvas.height= height;
this.width = canvas.width;
this.height = canvas.height;
this.depth = depth;
this.shaderGPU= 0;
this.program = this.context.createProgram();
}
set assemble(str) {
this.context.shaderSource(this.shader, str);
this.context.compileShader(this.shader);
this.context.attachShader(this.program, this.shader);
if( ++this.shaderGPU == 2 ) {
this.context.linkProgram(this.program);
this.context.useProgram(this.program);
this.context.enable(this.context.DEPTH_TEST);
this.context.viewport(0, 0, this.width, this.height);
console.log("Shader Run: "+this.shaderGPU)
}
}
set vertext(code) {
this.shader = this.context.createShader(this.context.VERTEX_SHADER);
this.assemble = code;
}
set fragment(code) {
this.shader = this.context.createShader(this.context.FRAGMENT_SHADER);
this.assemble = code;
}
set scale(name) { // remember the scalename to be used in projector
let location = this.context.getUniformLocation(this.program, name);
this.context.uniform3f(location, this.width, this.height, this.depth);
this.scalename = name;
}
uniform(name) { // use fat arrow function ()=>{} to bind this with canvas3D object
let location = this.context.getUniformLocation(this.program, name);
return { transfer:(m) => { this.context.uniformMatrix4fv(location, false, m); } }
}
attribute(name) { // transfer the attributes one by one
let location = this.context.getAttribLocation(this.program, name);
return { transfer:(size) => {
this.context.vertexAttribPointer(location, size, this.context.FLOAT, false, 0, 0);
this.context.enableVertexAttribArray(location);
}
}
}
adddata(array) {
let buffer = this.context.createBuffer();
this.context.bindBuffer(this.context.ARRAY_BUFFER, buffer);
this.context.bufferData(this.context.ARRAY_BUFFER, new Float32Array(array), this.context.STATIC_DRAW);
return buffer;
}
indexbuffer(array) {
this.index = this.context.createBuffer();
this.context.bindBuffer(this.context.ELEMENT_ARRAY_BUFFER, this.index);
this.context.bufferData(this.context.ELEMENT_ARRAY_BUFFER, new Uint16Array(array), this.context.STATIC_DRAW);
this.length = array.length;
return this;
}
pointbuffer(array) {
this.buffer = this.adddata(array);
this.context.bindBuffer(this.context.ARRAY_BUFFER, this.buffer);
return this;
}
colorbuffer(array) {
this.color = this.adddata(array);
this.context.bindBuffer(this.context.ARRAY_BUFFER, this.color);
return this;
}
get draw() {
this.context.bindBuffer(this.context.ELEMENT_ARRAY_BUFFER, this.index);
this.context.drawElements(this.context.TRIANGLES , this.length , this.context.UNSIGNED_SHORT,0);
}
get clear() { this.context.clear(this.context.COLOR_BUFFER_BIT | this.context.DEPTH_BUFFER_BIT); return this; }
projector(θ, near, far) {
return new matriX4().projector(θ, near, far, this.width, this.height);
}
}
let vertex = [
-1,-1,-1,
1,-1,-1,
1, 1,-1,
-1, 1,-1,
-1,-1, 1,
1,-1, 1,
1, 1, 1,
-1, 1, 1,
-1,-1,-1,
-1, 1,-1,
-1, 1, 1,
-1,-1, 1,
1,-1,-1,
1, 1,-1,
1, 1, 1,
1,-1, 1,
-1,-1,-1,
-1,-1, 1,
1,-1, 1,
1,-1,-1,
-1, 1,-1,
-1, 1, 1,
1, 1, 1,
1, 1,-1,
];
let colors = [
5,3,7, 5,3,7, 5,3,7, 5,3,7,
1,1,3, 1,1,3, 1,1,3, 1,1,3,
0,0,1, 0,0,1, 0,0,1, 0,0,1,
1,0,0, 1,0,0, 1,0,0, 1,0,0,
1,1,0, 1,1,0, 1,1,0, 1,1,0,
0,1,0, 0,1,0, 0,1,0, 0,1,0
];
let indices= [
0,1,2, 0,2,3, 4,5,6, 4,6,7,
8,9,10, 8,10,11, 12,13,14, 12,14,15,
16,17,18, 16,18,19, 20,21,22, 20,22,23
];
var my = new canvas3D(500, 500, 500);
my.vertext = 'attribute vec3 position;attribute vec3 color;'+
'uniform mat4 Pmatrix;uniform mat4 Vmatrix;uniform mat4 Mmatrix;'+
'varying vec3 vColor;'+
'void main(void) { gl_Position = Pmatrix*Vmatrix*Mmatrix*vec4(position, 1.);vColor = color;}';
my.fragment = 'precision mediump float;'+
'varying vec3 vColor;'+
'void main(void) { gl_FragColor = vec4(vColor, 1.);}';
let abs = (x) => { return x < 0 ? -x:x;}
let z = -10; // back camera off in z direction to see target
let θ = π/2 ; // field of view angle = 90 degree for the camera
let near = 1; // nearest distance between camera and target in z direction
let far = near + abs(z); // farest distance between camera and target in z direction
let Δz = z; // camera back off
let Δy = abs(Δz)*tan(θ/2); // camera pull up
my.indexbuffer(indices);
my.colorbuffer(colors).attribute('color').transfer(3); // 3 per time
my.pointbuffer(vertex).attribute('position').transfer(3); // 3 per time
my.uniform("Pmatrix").transfer(my.projector(θ, near, far).m); // perspective project
my.uniform("Vmatrix").transfer(new matriX4().translate(0,Δy,Δz).rotatex(θ).m);// camera matrix
let mat4GPU = my.uniform("Mmatrix"); // get GPU mat4 location
let matrix = new matriX4().rotatex(π/4).rotatey(π/4); // matrix of the target to use in animatiton
let Δθ = π/5;
let Δs = 1;
var animatiton = function() {
mat4GPU.transfer(matrix.translatez(Δs).rotatez(Δθ).m);
my.clear.draw;
z += Δs ;
console.log(abs(z)+":"+abs(Δz)) ;
if( abs(z) <= abs(Δz) ) setTimeout(animatiton, 500);
}
animatiton();
</script></body></html>
2018年10月29日 星期一
2018年10月28日 星期日
VS Code 關閉惱人的提示訊息
在設定欄(File -> Preferences -> Settings ), 修改 Uert Settings 增加:
"editor.hover.enabled":false,
"editor.minimap.enabled": false,
"update.channel": "none"
然後存檔就可以了.
2018年10月13日 星期六
寫一個簡單在 HTML canvas 裡繪圖的 Javascript 程式
<html><head></head><body><script>
// 存成 draw.html 檔案後, 用支援 ES6 的瀏覽器開啟
class myCanvas{
constructor(name,maxx,maxy){
let div = document.createElement('div');
let canvas = document.createElement('canvas');
let ctx = canvas.getContext("2d");
let textnode= document.createTextNode(name);
document.body.appendChild(textnode);
document.body.appendChild(div);
div.appendChild(canvas);
div.align="center";
canvas.style.border = "1px solid";//外框 1 點
canvas.width = maxx; //{0 < x < maxx}
canvas.height = maxy; //{-maxy < y < maxy}
this.context = ctx; // must init first, will be used later
this._ispaint = false; // must init second
this.limitx = canvas.width;
this.limity = canvas.height;
this.yscale = 95/100; // 95%
this.color = "green";
this.dashline = [4,1]; // 4px solid, 1px space
this.drawline(0, 0, maxx, 0);
this.drawline(0, -maxy, 0, maxy);
this.color = "red";
this.dashline = [1,4]; // 1px solid, 4px space
this.drawline(0, -maxy, maxx, -maxy);
this.drawline(0 , maxy, maxx, maxy);
this.dashline = [5,0]; // 5px solid
this.drawtext("0",0,0);
this.drawtext( "x="+maxx, maxx, 0);
this.drawtext( "+"+maxy, 0,-maxy);
this.drawtext( "-"+maxy, 0, maxy);
} // object create complete
// following method need object to be created first
set start(ispaint) { // keep track of the paint state
if( ispaint ) {
if( ! this.start ) this.context.beginPath();
this._ispaint = true;
} else {
this.move(0,0);// back to the origin
if( this.start ) this.context.stroke();
this._ispaint = false;
}
}
get start() { return this._ispaint; }
set color(c){ this.context.strokeStyle=c;}
set dashline(x){this.context.setLineDash(x); }
close() { this.start = false; }
move(x,y) { this.context.moveTo(x,y); }
rescale(sx,sy){ this.context.scale(sx, sy); }
mirror() { this.context.scale(1, -1); }
set yscale(sy){
// 先移動原點至中心, y 再縮小比例並鏡射
this.context.lineWidth = 1; // 線寬 1 點
this.context.translate(0, this.limity/2); // y center
this.rescale(1, -sy/2); //
//ctx.rotate(5*Math.PI/180);// 座標旋轉角度
}
drawline(x1,y1,x2,y2){
if( ! this.start ) this.start = true
this.context.moveTo(x1,y1);
this.context.lineTo(x2,y2);
this.start = false; // auto offline
}
lineto(x, y){
if( ! this.start ) this.start = true
this.context.lineTo(x,y);
}
drawtext(msg, x, y, align){
this.rescale(1, -1);// text force y mirror back
let fontpx=24;
if( (x + msg.length * fontpx) >= this.limitx ) {
this.context.textAlign="right";
} else this.context.textAlign="left";
if( y<=0 ) y = y + fontpx;
this.context.font=fontpx + "px Comic Sans MS";
this.context.fillText(msg, x, y);// 充實字體
this.rescale(1, -1);// mirror back again
}
};
let pen = new myCanvas("Sin",600,300);
pen.color="blue";
for (let x=0; x < pen.limitx; x++) {
pen.lineto(x, pen.limity * Math.sin(2*Math.PI*x*4/pen.limitx));
}
pen.close();
</script></body></html>
// 存成 draw.html 檔案後, 用支援 ES6 的瀏覽器開啟
class myCanvas{
constructor(name,maxx,maxy){
let div = document.createElement('div');
let canvas = document.createElement('canvas');
let ctx = canvas.getContext("2d");
let textnode= document.createTextNode(name);
document.body.appendChild(textnode);
document.body.appendChild(div);
div.appendChild(canvas);
div.align="center";
canvas.style.border = "1px solid";//外框 1 點
canvas.width = maxx; //{0 < x < maxx}
canvas.height = maxy; //{-maxy < y < maxy}
this.context = ctx; // must init first, will be used later
this._ispaint = false; // must init second
this.limitx = canvas.width;
this.limity = canvas.height;
this.yscale = 95/100; // 95%
this.color = "green";
this.dashline = [4,1]; // 4px solid, 1px space
this.drawline(0, 0, maxx, 0);
this.drawline(0, -maxy, 0, maxy);
this.color = "red";
this.dashline = [1,4]; // 1px solid, 4px space
this.drawline(0, -maxy, maxx, -maxy);
this.drawline(0 , maxy, maxx, maxy);
this.dashline = [5,0]; // 5px solid
this.drawtext("0",0,0);
this.drawtext( "x="+maxx, maxx, 0);
this.drawtext( "+"+maxy, 0,-maxy);
this.drawtext( "-"+maxy, 0, maxy);
} // object create complete
// following method need object to be created first
set start(ispaint) { // keep track of the paint state
if( ispaint ) {
if( ! this.start ) this.context.beginPath();
this._ispaint = true;
} else {
this.move(0,0);// back to the origin
if( this.start ) this.context.stroke();
this._ispaint = false;
}
}
get start() { return this._ispaint; }
set color(c){ this.context.strokeStyle=c;}
set dashline(x){this.context.setLineDash(x); }
close() { this.start = false; }
move(x,y) { this.context.moveTo(x,y); }
rescale(sx,sy){ this.context.scale(sx, sy); }
mirror() { this.context.scale(1, -1); }
set yscale(sy){
// 先移動原點至中心, y 再縮小比例並鏡射
this.context.lineWidth = 1; // 線寬 1 點
this.context.translate(0, this.limity/2); // y center
this.rescale(1, -sy/2); //
//ctx.rotate(5*Math.PI/180);// 座標旋轉角度
}
drawline(x1,y1,x2,y2){
if( ! this.start ) this.start = true
this.context.moveTo(x1,y1);
this.context.lineTo(x2,y2);
this.start = false; // auto offline
}
lineto(x, y){
if( ! this.start ) this.start = true
this.context.lineTo(x,y);
}
drawtext(msg, x, y, align){
this.rescale(1, -1);// text force y mirror back
let fontpx=24;
if( (x + msg.length * fontpx) >= this.limitx ) {
this.context.textAlign="right";
} else this.context.textAlign="left";
if( y<=0 ) y = y + fontpx;
this.context.font=fontpx + "px Comic Sans MS";
this.context.fillText(msg, x, y);// 充實字體
this.rescale(1, -1);// mirror back again
}
};
let pen = new myCanvas("Sin",600,300);
pen.color="blue";
for (let x=0; x < pen.limitx; x++) {
pen.lineto(x, pen.limity * Math.sin(2*Math.PI*x*4/pen.limitx));
}
pen.close();
</script></body></html>
2018年10月3日 星期三
投資債券利用 Javascript 計算報酬率
假設想購買一檔債券基金, 手續費 2.4%, 計劃投入資金一年, 每個月購買 3000 圓美金的基金(假設一年內的基金市值沒有變化), 一共付了 12 期費用, 債券基金年配息 5.35%, 每月領息, 繳款期間不領息, 直接投入基金, 當繳費期滿一年後, 持續領息至少再花一年, 未來當債券價格回到當初投入價格時才全部贖回, 試計算獲利率, 並換算一下 IRR 的年化報酬率:
// debt.js
function irr(CT) {
var maxrate = 100/100 // 100%
var minrate = 0.0
var k =1000 // prevent infinit loop
var NPV,IRR
while (k-- > 0) {
[NPV, IRR] = [0.0, (maxrate + minrate) / 2];
CT.forEach( (c,i) => NPV = NPV + c/((1+IRR)**i) );
if (Math.abs(NPV) < 1e-6 || (maxrate - minrate) < 1e-6) break;
if (NPV > 0) minrate = IRR // too small, increase IRR by changing low bound
else maxrate = IRR // too big, decrease IRR by changing high bound
}
return [NPV, IRR]
}
// 投資債券期間現金流量
var CT = [ ] // 初始化現金流量矩陣
var paytime = 12; // 投資一年,分期付款
var gan = 2*paytime + 1; // 投資領息期限
var C0 = 3e3; // 每期投入資金
var r = 5.35/paytime/100; // 每期債息利率, 年利率=5.35%, debt rate
var FUND = C0 * (1 - 2.4/100);// 手續費率/每期 = 2.4%, pay discount
var BC = FUND; // 初期基金
var COST = C0;
var S = 0.0;
CT.push(-C0); // 初期費用
for (var i = 1; i < gan; i++) { // 續期現金流量
income = BC*r // 每期收入
if (i == (gan - 1)) CT.push(BC); // 期末贖回全部基金
else if (i < paytime) { // 續期繳費
CT.push(income - C0);// 現金流量 = 利息收入 - 每期應付資金
COST = COST + C0 - income;// 累計成本
BC = BC + FUND; // 基金累積
} else {
CT.push(income);
S = S + income;// 累積利息
}
}
CT.forEach( (x,i) => console.log("第" + (i+1) + "期淨收 " + Math.round(x*10)/10) );
console.log("=================\n實繳 " + Math.round(COST*10)/10 + "\t,利息收入 " + Math.round(S*10)/10);
console.log("投資共" + Math.round(gan*100/paytime)/100 + "年,報酬率: " + Math.round(S*1e4/COST)/1e2 + "%");
var [v, r] = irr(CT);
console.log("換算 IRR 年化報酬率: " + Math.round(r*paytime*1e4)/1e2 + "%" + "\t,NPV = " + v);
執行 js debt 看輸出結果:
第1期淨收 -3000
第2期淨收 -2986.9
第3期淨收 -2973.9
第4期淨收 -2960.8
第5期淨收 -2947.8
第6期淨收 -2934.7
第7期淨收 -2921.7
第8期淨收 -2908.6
第9期淨收 -2895.6
第10期淨收 -2882.5
第11期淨收 -2869.5
第12期淨收 -2856.4
第13期淨收 156.6
第14期淨收 156.6
第15期淨收 156.6
第16期淨收 156.6
第17期淨收 156.6
第18期淨收 156.6
第19期淨收 156.6
第20期淨收 156.6
第21期淨收 156.6
第22期淨收 156.6
第23期淨收 156.6
第24期淨收 156.6
第25期淨收 35136
=================
實繳 35138.4 ,利息收入 1879.8
投資共2.08年,報酬率: 5.35%
換算 IRR 年化報酬率: 3.43% ,NPV = -0.2703545208569267
// debt.js
function irr(CT) {
var maxrate = 100/100 // 100%
var minrate = 0.0
var k =1000 // prevent infinit loop
var NPV,IRR
while (k-- > 0) {
[NPV, IRR] = [0.0, (maxrate + minrate) / 2];
CT.forEach( (c,i) => NPV = NPV + c/((1+IRR)**i) );
if (Math.abs(NPV) < 1e-6 || (maxrate - minrate) < 1e-6) break;
if (NPV > 0) minrate = IRR // too small, increase IRR by changing low bound
else maxrate = IRR // too big, decrease IRR by changing high bound
}
return [NPV, IRR]
}
// 投資債券期間現金流量
var CT = [ ] // 初始化現金流量矩陣
var paytime = 12; // 投資一年,分期付款
var gan = 2*paytime + 1; // 投資領息期限
var C0 = 3e3; // 每期投入資金
var r = 5.35/paytime/100; // 每期債息利率, 年利率=5.35%, debt rate
var FUND = C0 * (1 - 2.4/100);// 手續費率/每期 = 2.4%, pay discount
var BC = FUND; // 初期基金
var COST = C0;
var S = 0.0;
CT.push(-C0); // 初期費用
for (var i = 1; i < gan; i++) { // 續期現金流量
income = BC*r // 每期收入
if (i == (gan - 1)) CT.push(BC); // 期末贖回全部基金
else if (i < paytime) { // 續期繳費
CT.push(income - C0);// 現金流量 = 利息收入 - 每期應付資金
COST = COST + C0 - income;// 累計成本
BC = BC + FUND; // 基金累積
} else {
CT.push(income);
S = S + income;// 累積利息
}
}
CT.forEach( (x,i) => console.log("第" + (i+1) + "期淨收 " + Math.round(x*10)/10) );
console.log("=================\n實繳 " + Math.round(COST*10)/10 + "\t,利息收入 " + Math.round(S*10)/10);
console.log("投資共" + Math.round(gan*100/paytime)/100 + "年,報酬率: " + Math.round(S*1e4/COST)/1e2 + "%");
var [v, r] = irr(CT);
console.log("換算 IRR 年化報酬率: " + Math.round(r*paytime*1e4)/1e2 + "%" + "\t,NPV = " + v);
執行 js debt 看輸出結果:
第1期淨收 -3000
第2期淨收 -2986.9
第3期淨收 -2973.9
第4期淨收 -2960.8
第5期淨收 -2947.8
第6期淨收 -2934.7
第7期淨收 -2921.7
第8期淨收 -2908.6
第9期淨收 -2895.6
第10期淨收 -2882.5
第11期淨收 -2869.5
第12期淨收 -2856.4
第13期淨收 156.6
第14期淨收 156.6
第15期淨收 156.6
第16期淨收 156.6
第17期淨收 156.6
第18期淨收 156.6
第19期淨收 156.6
第20期淨收 156.6
第21期淨收 156.6
第22期淨收 156.6
第23期淨收 156.6
第24期淨收 156.6
第25期淨收 35136
=================
實繳 35138.4 ,利息收入 1879.8
投資共2.08年,報酬率: 5.35%
換算 IRR 年化報酬率: 3.43% ,NPV = -0.2703545208569267
訂閱:
文章 (Atom)