Skip to content

Commit

Permalink
Initial import
Browse files Browse the repository at this point in the history
  • Loading branch information
Jon Eyrick authored Feb 12, 2019
1 parent c3c4d49 commit 4002e34
Show file tree
Hide file tree
Showing 2 changed files with 271 additions and 0 deletions.
113 changes: 113 additions & 0 deletions chartaholic.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
#chart {
margin: auto auto;
width: 90vw;
height: 80vh;
}

body {
background: #1c1e23;
color: #fff;
margin: 0;
padding: 0;
font-family: sans-serif;
font-size: 14px;
overflow-x: hidden
}

.candlechart {
margin: auto auto;
position: relative;
padding-left: 40px;
padding-bottom: 40px;
}

svg {
/* filter: drop-shadow( -1px 2px 1px rgba(0,0,0,0.75)); */
margin: auto auto;
margin-top: 60px;
fill: none;
/* stroke: #08f; */
display: block;
stroke-width: 5px;
/* border-left: 1px solid rgba(0, 0, 0, 0.3);
border-bottom: 1px solid rgba(0, 0, 0, 0.3); */
border:1px solid rgba(0,0,0,0.3);
font-family: "Lucida Grande", "Lucida Sans Unicode", Arial, Helvetica, sans-serif;
font-size: 12px;
box-shadow: inset 0 2px 10px rgba(0, 0, 0, 0.1)
}

polyline {
/*
stroke:#006600;fill:#33cc33
*/
stroke: #6d1e7f;
fill: #6d1e7f;
stroke-width: 1px;
opacity: 0.2
}

.wick {
stroke-width: 1px;
}

.label text {
font-size: 11px;
font-weight: normal;
color: #FFFFFF;
fill: #FFFFFF
}

#mousex,
#mousey {
font-size: 11px;
font-weight: bold;
color: #E91E63;
fill: #E91E63
}

text {
fill: #fff;
/*tv blue #2196f3 grey #6c757d;*/
font: bold 16px sans-serif;
text-shadow: 1px 1px #000;
}

.headline {
text-shadow: 2px 2px rgba(0, 0, 0, 0.75);
}

.horizontal {
opacity: 1;
stroke-width: 1px;
stroke: #6c757d;
fill: #6c757d
}

.blue .down {
stroke: #3c78d8;
fill: #3c78d8;
}

.blue .up {
stroke: #fff;
fill: #fff;
}

.light .up {
stroke: #fff;
fill: #fff
}

.light .down {
fill: #000;
stroke: #000
}

.standard .up {
stroke: #399F6E
}

.standard .down {
stroke: #FE4747
}
158 changes: 158 additions & 0 deletions chartaholic.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
class Chartaholic {
reset() {
this.zoom = 0;
this.count = 0;
this.data = [];
this.opens = [];
this.highs = [];
this.lows = [];
this.closes = [];
this.times = [];
}

// ticks: pass an array of ohlc values.
// keys: default open, high, low, close, time
import( ticks ) {
for ( let key of ['open', 'high', 'low', 'close', 'time'] ) {
if ( typeof this.keys[key] === "undefined" ) this.keys[key] = key;
}
this.reset();
for ( let tick of ticks ) {
let time = new Date( tick[this.keys.time] ).getTime();
let open = Number( tick[this.keys.open] );
let high = Number( tick[this.keys.high] );
let low = Number( tick[this.keys.low] );
let close = Number( tick[this.keys.close] );
this.opens.push( open );
this.highs.push( high );
this.lows.push( low );
this.closes.push( close );
this.times.push( time );
this.data.push( {
x: this.count++,
o: open,
h: high,
l: low,
c: close,
time: time
} );
}
}

resize() {
this.width = this.target.clientWidth;
this.height = this.target.clientHeight;
this.render();
}

scroll( event ) {
const delta = Math.sign( event.deltaY );
this.zoom += delta;
this.render();
}

dx( x ) {
return Math.round( ( x - this.min_x ) / this.delta_x * this.width );
}
dy( y ) {
return Math.round( this.height - ( y - this.min_y ) / this.delta_y * this.height );
}

render( json = false ) {
this.width = this.target.clientWidth;
this.height = this.target.clientHeight;
if ( !json ) json = this.data;
let data = json.slice( this.zoom * -7 );
//console.info( data );
this.wickpadding = this.width > 1500 ? 4 : this.width > 900 ? 3 : 2;
this.wickwidth = Math.round( this.width / data.length ) - this.wickpadding;
if ( this.wickwidth > 50 ) this.wickwidth = 50;
this.min_x = Math.min( ...data.map( d => d.x ) );
this.max_x = Math.max( ...data.map( d => d.x ) ) + 0.5;
this.min_y = Math.min( ...data.map( d => d.l ) );
this.max_y = Math.max( ...data.map( d => d.h ) );
this.mid_y = ( this.max_y + this.min_y ) / 2;
this.delta_y = this.max_y - this.min_y;
this.delta_x = this.max_x - this.min_x;
//console.log( `delta: ${this.delta_x}, ${this.delta_y}` );
//console.info( `min_x: ${this.min_x}, min_y: ${this.min_y}, max_x: ${this.max_x}, max_y: ${this.max_y}` );
//let x_ticks = scaleTicks(TICK_COUNT, MAX_X, MIN_X);
//let y_ticks = [MAX_Y, (MAX_Y + MIN_Y) / 2, MIN_Y];
//let dateFormat = 'll'; // https://momentjs.com
//let xticks = show_time ? quartileBounds(data.map(d => d.time)).map(v => `<div data-value='${moment(v).format(dateFormat)}'></div>`) : x_ticks.map(v => `<div data-value='${format(v, precision)}'></div>`);
//let yticks = MAX_Y > 999 ? y_ticks.map(v => `<div data-value='${usd(v)}'></div>`) : y_ticks.map(v => `<div data-value='${format(v, precision)}'></div>`);
let namespace = "http://www.w3.org/2000/svg", svg = document.createElementNS( namespace, "svg" );
svg.style.strokeWidth = `${this.wickwidth}px`;
svg.style.width = `${this.width}px`;
svg.style.height = `${this.height}px`;
svg.setAttributeNS( null, "class", this.theme );
svg.setAttributeNS( null, "width", this.width );
svg.setAttributeNS( null, "height", this.height );
for ( let tick of data ) {
let color = tick.c >= tick.o ? 'up' : 'down';
let openy = this.dy( tick.o ), closey = this.dy( tick.c );
if ( Math.abs( openy - closey ) < 2 ) openy += 2;
let wick = document.createElementNS( namespace, "path" ), body = document.createElementNS( namespace, "path" );
wick.setAttributeNS( null, 'class', `wick ${color}` );
wick.setAttributeNS( null, 'd', `M${this.dx( tick.x )},${this.dy( tick.h )}L${this.dx( tick.x )},${this.dy( tick.l )}` );
body.setAttributeNS( null, 'class', color );
body.setAttributeNS( null, 'd', `M${this.dx( tick.x )},${openy}L${this.dx( tick.x )},${closey}` );
svg.appendChild( wick );
svg.appendChild( body );
//ticks += `<path class='wick ${color}' d='M${this.dx( tick.x )},${this.dy( tick.h )}L${this.dx( tick.x )},${this.dy( tick.l )}'/>
// <path class='${color}' d='M${this.dx( tick.x )},${openy}L${this.dx( tick.x )},${closey}'/>`;
}
if ( this.title ) {
//ticks += `<text alignment-baseline='hanging' x='0' y='1' class='headline'>${this.title}</text>`;
let text = document.createElementNS( namespace, "text" );
text.setAttributeNS( null, "class", "headline" );
text.setAttributeNS( null, "alignment-baseline", "hanging" );
text.setAttributeNS( null, "x", "0" );
text.setAttributeNS( null, "y", "1" );
text.innerHTML = this.title;
svg.appendChild( text );
}
//onmousemove='debounce(mousemove(event),20)' onmouseout='mouseout()'
//<text id='mousex' alignment-baseline='hanging' x='0' y='0'></text>
//<text id='mousey' alignment-baseline='baseline' x='0' y='${HEIGHT}'></text>
//this.target.onresize = this.resize.bind( this );
this.target.innerHTML = "";
this.target.appendChild( svg );
//return this.target.innerHTML = `<svg width='${this.width}' height='${this.height}' class='${this.theme}' style='stroke-width:${this.wickwidth}px' onresize='this.resize'>${ticks}</svg>`;
}

getTicks( count, max ) {
return [...Array( count ).keys()].map( d => max / ( count - 1 ) * parseInt( d ) )
}
scaleTicks( count, max, min ) {
return [...Array( count ).keys()].map( d => max / ( count - 1 ) * parseInt( d ) )
//return [...Array(count).keys()].map(d => (max - min) / (count - 1) * parseInt(d))
}
nz( value, def = 0 ) {
return isFinite( value ) ? value : def;
}
usd( number, maxdigits = 2, mindigits = 0 ) {
return new Intl.NumberFormat( 'en-US', { style: 'currency', currency: 'USD', minimumFractionDigits: mindigits, maximumFractionDigits: maxdigits } ).format( number );
}
format( number, maxPrecision = 8, minPrecision = 0 ) {
return new Intl.NumberFormat( 'en-US', { style: 'decimal', minimumFractionDigits: minPrecision, maximumFractionDigits: maxPrecision } ).format( number );
}

constructor( target, options = {} ) {
this.zoom = 0; //31
this.target = typeof target == "string" ? document.querySelector( target ) : target;
this.theme = typeof options.theme == "undefined" ? "standard" : options.theme;
this.title = typeof options.title == "undefined" ? "" : options.title;
//this.width = typeof options.width == "undefined" ? window.innerWidth * 0.9 : options.width;
//this.height = typeof options.height == "undefined" ? window.innerHeight * 0.8 : options.height;
this.keys = typeof options.keys == "undefined" ? {} : options.keys;
this.reset();
window.addEventListener( 'resize', this.resize.bind( this ) );
this.target.addEventListener( 'wheel', this.scroll.bind( this ) );
if ( typeof options.ticks !== "undefined" ) {
this.import( options.ticks );
this.render();
}
}
}
if ( typeof module !== 'undefined' && module.exports ) module.exports = Chartaholic;

0 comments on commit 4002e34

Please sign in to comment.