var { ChartCanvas, Chart, DataSeries, OverlaySeries, EventCapture } = ReStock;
var { CandlestickSeries, HistogramSeries, LineSeries, AreaSeries, StochasticSeries } = ReStock.series;
var { MouseCoordinates, CurrentCoordinate } = ReStock.coordinates;
var { EdgeContainer, EdgeIndicator } = ReStock.coordinates;
var { TooltipContainer, OHLCTooltip, MovingAverageTooltip, StochasticTooltip } = ReStock.tooltip;
var { StockscaleTransformer } = ReStock.transforms;
var { XAxis, YAxis } = ReStock.axes;
var { MACD, EMA, SMA, FullStochasticOscillator } = ReStock.indicator;
var { ChartWidthMixin, TypeChooser } = ReStock.helper;
window.Histogram = React.createClass({
mixins: [ChartWidthMixin],
propTypes: {
data: React.PropTypes.array.isRequired,
type: React.PropTypes.oneOf(["svg", "hybrid"]).isRequired,
},
getDefaultProps() {
return {
type: "svg"
}
},
render() {
var width = this.props.width || this.state !== null && this.state.width;
if (!width) return <div />;
var { data, type } = this.props;
var dateFormat = d3.time.format("%Y-%m-%d");
return (
<ChartCanvas width={width} height={400}
margin={{left:100, right: 100, top:30, bottom: 30}} initialDisplay={100}
dataTransform={[ { transform: StockscaleTransformer } ]}
data={data} type={type}>
<Chart id={1} yMousePointerDisplayLocation="right"
yMousePointerDisplayFormat={d3.format(".4s")} >
<XAxis axisAt="bottom" orient="bottom" />
<YAxis axisAt="right" orient="right" ticks={5} tickFormat={d3.format("s")} />
<DataSeries id={1} yAccessor={(d) => d.close} >
<LineSeries/>
</DataSeries>
</Chart>
<CurrentCoordinate forChart={1} forDataSeries={1} />
<Chart id={2} yMousePointerDisplayLocation="left" yMousePointerDisplayFormat={d3.format(".4s")} >
<YAxis axisAt="left" orient="left" ticks={5} />
<DataSeries id={0} yAccessor={(d) => d.volume} >
<HistogramSeries baseAt={(xScale, yScale, d) => yScale(0)} fill={(d) => d.volume > 0 ? "#6BA583" : "red"} />
</DataSeries>
</Chart>
<CurrentCoordinate forChart={2} forDataSeries={0} />
<EdgeContainer>
<EdgeIndicator itemType="last" orient="right"
edgeAt="right" forChart={1} forDataSeries={1} />
<EdgeIndicator itemType="first" orient="left"
edgeAt="left" forChart={1} forDataSeries={1} />
<EdgeIndicator itemType="first" orient="left"
edgeAt="left" forChart={1} forDataSeries={1} />
</EdgeContainer>
<MouseCoordinates xDisplayFormat={dateFormat} type="crosshair" />
<EventCapture mouseMove={true} zoom={true} pan={true} mainChart={1} defaultFocus={false} />
<TooltipContainer>
<OHLCTooltip forChart={1} />
<MovingAverageTooltip forChart={1} onClick={(e) => console.log(e)} origin={[-38, 5]} />
</TooltipContainer>
</ChartCanvas>
);
}
});
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=10">
<title>React Stockcharts - CandleStickChartWithFullStochasticsIndicator Example</title>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js"></script>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/react/0.13.3/react.js"></script>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/react/0.14.0-rc1/react-dom.min.js"></script>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.25/browser.js"></script>
<script type="text/javascript" src="//rrag.github.io/react-stockcharts/dist/react-stockcharts.js"></script>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/flux/2.1.1/Flux.min.js"></script>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.0.0-alpha1/jquery.min.js"></script>
<link rel="stylesheet" type="text/css" href="main.css">
<script type="text/javascript">
d3.select(self.frameElement).style("height", "800px");
</script>
</head>
<body>
<div id="chart"></div>
<script type="text/babel" src="EventEmitterMixin.js"></script>
<script type="text/babel" src="Histogram.jsx"></script>
<script type="text/babel" src="Chart.jsx"></script>
<script type="text/babel" src="Table.jsx"></script>
<script type="text/babel" src="main.jsx"></script>
</body>
</html>
var Histogram = window.Histogram;
var parseDate = d3.time.format("%d-%b-%Y").parse;
var EventEmitterMixin = window.EventEmitterMixin;
window.Chart = React.createClass({
propTypes: {
data: React.PropTypes.array.isRequired,
},
mixins:[EventEmitterMixin],
getInitialState() {
return {
data: [],
currentIndex:2
}
},
componentDidMount() {
this.eventEmitter('on','rowChange', function(index){
this.setState({ currentIndex: index});
});
},
getTitle(currentIndex){
return (this.props.data) ? this.props.data[currentIndex].split(',').splice(0,1): "Chart";
},
getData(currentIndex){
var items = this.props.data;
if(!items) return {};
var dates = items[0].split(',').slice(2, items.length);
var totals = items[1].split(',').slice(2, items.length);
var volume = items[currentIndex].split(',').slice(2, items.length);
var chartItems = [];
for (var i = 0; i < volume.length; i++){
chartItems.push(
{ adjclose:0,
close:parseInt(totals[i]),
date:new Date(parseDate(dates[i]).getTime()),
high:0,
low:0,
open:0,
volume:parseInt(volume[i]) }
);
}
return chartItems;
},
render() {
return (
<div>
<h3 className="centered">{this.getTitle(this.state.currentIndex)}</h3>
<Histogram data={this.getData(this.state.currentIndex)} type="hybrid" />
</div>
);
}
});
var PnlChart = window.PnlChart;
var Table = window.Table;
var App = React.createClass({
componentDidMount: function() {
},
render: function() {
return (
<div>
<Table />
</div>
);
}
});
React.render(<App/>, document.getElementById('chart'));
var EventEmitterMixin = window.EventEmitterMixin;
var Chart = window.Chart;
var numFormat = d3.format("0,000");
window.Table = React.createClass({
mixins:[EventEmitterMixin],
getInitialState() {
return{
data : "",
currentID: 2
}
},
componentDidMount() {
var scope = this;
this.eventEmitter('on','loadedData', function(data){
this.setState({ data: data});
});
if (!this.state.data) {
$.ajax({
type: 'GET',
url: 'tabledata.json',
async: false,
contentType: "application/json",
dataType: 'json',
success: function (data) {
scope.eventEmitter('emit','loadedData', data);
},
error: function (e) {
alert('error');
console.log(e);
}
});
}
},
onClick(e){
var newId = e.currentTarget.id;
if (this.state.currentID !== newId)
this.eventEmitter('emit','rowChange', newId);
},
getTableData(items){
let tableItems = [];
for (let i = 0; i < items.length; i++){
let colArr = items[i].split(',');
tableItems.push(
<tr key={"pnl chart" + i} id={i} onClick={e => this.onClick(e)}>
{ colArr.map((x, j) =>
<td key={x+j}>{ isNaN(x) ? x : numFormat(x)}</td>
)}
</tr>
);
}
return tableItems;
},
render() {
return (
<div>
{ this.state.data ?
<div>
<table className="dataTable">
<tbody>{this.getTableData(this.state.data.items)}</tbody>
</table>
<Chart data={this.state.data.items} />
</div>
: " "}
</div>
);
}
});
{"items":[
"Manager,fYTD,14-Dec-2014,15-Jan-2015,15-Feb-2015,15-Mar-2015,15-Apr-2015,15-May-2015,15-Jun-2015,15-Jul-2015,15-Aug-2015,15-Sep-2015,15-Oct-2015,15-Nov-2015",
"Totals,8109,7776,-698,-1026,297,5771,5838,1945,7178,899,5608,-2339,-3605",
"Bob,837,-2653,5290,-8449,-2048,-8683,-3504,7339,-6802,-7573,7698,2953,-2834",
"Jenny,-1679,-1899,-2233,-7443,-4745,-5438,6197,-2694,16,-5221,113,-2092,4905",
"Core,1581,-4101,7754,-4133,698,-6748,5660,-6155,2387,5542,1423,7202,-9282",
"Stephan,4902,6073,-1114,1785,-9006,-3780,-3543,6232,-605,-1531,-306,4317,-5800",
"Qing,4117,7484,1882,8600,5062,-5327,4059,9199,7934,-3765,-9517,-2691,-6840",
"Tommy,-7892,1310,2066,-4129,-8200,7636,9992,1332,-6087,1014,-6964,-4848,1677",
"Jeremy,2072,5488,6390,-701,-25,-4517,5922,-7665,7975,4389,-1987,3390,3949",
"Mark,-6657,-4508,-6629,-9002,-787,7386,-9094,-2110,-7647,-9531,-2391,3226,-9377",
"Timothy,-3596,-6388,5319,-7081,507,3760,-5620,7796,-687,6193,7670,8613,4820",
"Jenkins,-8647,2756,26,-5907,-3649,9075,3922,9992,-6073,3583,-3766,-6139,-5454",
"Kat,1551,-7169,-4339,-1621,489,-285,9439,8796,6533,-6440,-5336,8429,8177",
"Unknown,9049,-2703,2343,-4938,-3913,6634,1312,7613,-5454,-2917,7490,-692,9325"
]}
'use strict';
var GLOBAL = typeof window === 'undefined' ? global : window;
var _events = GLOBAL.EVENT_EMITTER = {};
var _signCnt = 0;
var EventEmitter = {
mount(){
this._eventIncluded = [];
this._eventSign = '_eventTag_' + (_signCnt++);
},
unmount(){
this._eventIncluded.forEach(event => delete _events[event][this._eventSign]);
this._eventIncluded = [];
},
/**
* on
*
* @param {string} event :Event name
* @param {function} callback :Callback
*/
on(event,callback){
_events[event] || (_events[event] = {});
callback.self = this; //save current `this` reference
if(_events[event][this._eventSign]){
_events[event][this._eventSign].push(callback);
}else{
_events[event][this._eventSign] = [callback];
}
if( !~this._eventIncluded.indexOf(event) ){
this._eventIncluded.push(event);
}
},
/**
* one
*
* @param {string} event :Event name
* @param {function} callback :Callback
*/
one(event,callback){
var proxy = function(...data){
EventEmitter.off.call(this,event,proxy);
callback.apply(this,data);
};
EventEmitter.on.call(this,event,proxy);
},
/**
* off
*
* @param {string} event :Event name
* @param {function} callback :Callback
* @param {boolean} removeAll :Should remove event of all components
*/
off(event,callback,removeAll){
if(!_events[event]) return ;
if(removeAll){ //remove the event of all components
if(!callback){
_events[event] = {};
var currentRegi = this._eventIncluded.indexOf(event);
(~currentRegi) && this._eventIncluded.splice(currentRegi,1);
}else{
for(var _sign in _events[event]){
var index = _events[event][_sign].indexOf(callback);
(~index) && _events[event][_sign].splice(index,1);
if(!_events[event][_sign].length){
if(_sign == this._eventSign){
var currentRegi = this._eventIncluded.indexOf(event);
(~currentRegi) && this._eventIncluded.splice(currentRegi,1);
}
delete _events[event][_sign];
}
}
}
}else{ //only remove the event of current component
if(!_events[event][this._eventSign]) return ;
if(!callback){
if(_events[event][this._eventSign]){
delete _events[event][this._eventSign];
this._eventIncluded.splice(this._eventIncluded.indexOf(event),1);
}
}else if(_events[event][this._eventSign]){
var index = _events[event][this._eventSign].indexOf(callback);
(~index) && _events[event][this._eventSign].splice(index,1);
if(!_events[event][this._eventSign].length){
delete _events[event][this._eventSign];
this._eventIncluded.splice(this._eventIncluded.indexOf(event),1);
}
}
}
},
/**
* emit
*
* @param {string} event :Event name
*/
emit(event,...data){
if(!_events[event]) return ;
for(var _sign in _events[event]){
var list = _events[event][_sign].slice(0); //might be removed by nested `off`
list.forEach((cb)=>{
cb.apply(cb.self,data); // `this` point to component which bind this callback
});
list = null;
}
},
};
window.EventEmitterMixin = {
componentWillMount(){
EventEmitter.mount.call(this);
},
componentWillUnmount(){
EventEmitter.unmount.call(this);
},
eventEmitter(type,...data){
EventEmitter[type].apply(this,data);
},
};
.centered{
text-align:center;
}
table.dataTable {
border-spacing: 0px;
border-color: "grey";
font-size:12px;
}
table.dataTable td, th {
padding: 10px;
}
table.dataTable tr:nth-child(odd) {
background-color: #e0e0e0;
}
table.dataTable tr:first-child{
background: #0288d1;
color:#fff;
}
table.dataTable tr:hover {
background-color: #ffbf14;
}