programming – LPPL Market Watch http://localhost Blog of The Bubble Index Wed, 07 Feb 2018 02:25:07 +0000 en-US hourly 1 https://wordpress.org/?v=4.9.4 D3.js Multiple Line Plot – Part 6 https://lpplmarketwatch.com/2015/07/30/d3-js-multiple-line-plot-part-6/ https://lpplmarketwatch.com/2015/07/30/d3-js-multiple-line-plot-part-6/#respond Thu, 30 Jul 2015 02:32:54 +0000 https://lpplmarketwatch.com/?p=690 Working version of the plot, download here.

This final post describes the code which needs to be executed after any time-series has been added or removed. When a time-series has been added or removed via the legend button, it is possible that its unique range of y-values is not currently displayed in the plot. For instance, assume the “52 Days” and “104 Days” time-series are currently displayed and the y-range is: [0 – 123]. Now, the “2520 Days” time-series is added by a mouse click. This time series has y-values in the range [420 – 540]. Thus, the new range [0 – 540] needs to be established as the new y-axis. However, all the time-series displayed “pre-click” (52 and 104 Days) will need to be removed and re-drawn since their paths are scaled based on the [0 – 123] range of y-values. As seen in part 4, if the legend button is black with a strike-through, the addWindow() function is called:

function addWindow(name, addAllBoolean, updateBoolean, longTransition) {
    var transition = longTransition ? 500 : 0;
    if (svg.selectAll(".index").empty() || !svg.selectAll(".index").classed("a" + name) || updateBoolean) {
        var idName = name.toString();
        var tempIndex = svg.selectAll(".aaa")
            .data(indices.filter(function(d) {
                return d.name == idName;
            }))
            .enter().append("g")
            .attr("id", function(d) {
                return "index" + d.name;
            })
            .attr("class", function(d) {
                return "index a" + d.name;
            });
        if (!addAllBoolean) {
            var displayedIndices = d3.selectAll(".index").data();
            y.domain([
                d3.min(displayedIndices, function(c) {
                    return d3.min(c.values, function(v) {
                        return v.yvalue;
                    });
                }),
                d3.max(displayedIndices, function(c) {
                    return d3.max(c.values, function(v) {
                        return v.yvalue;
                    });
                })
            ]);
            svg.select(".y.axis")
                .call(yAxis);
            d3.selectAll(".index").data().forEach(function(d) {
                removeWindow(d.name, false, false);
                (d.name == name) ? addWindow(d.name, true, true, true): addWindow(d.name, true, true, false);

            });
        }
        tempIndex.append("path")
            .attr("class", "line")
            .attr("d", function(d) {
                return line(d.values);
            })
            .style("opacity", 0.0)
            .style("stroke", function(d) {
                return color(d.name);
            })
            .transition()
            .style("opacity", 1.0)
            .duration(transition);
    }
}

As can be seen by the addWindow() function, in order to add a window, all previous windows need to be removed. If the window is currently displayed, it can be removed with a mouse click. There are two types of addWindow() and removeWindow() function calls. The logic is based on various boolean values. This is needed because of the “Add All” and “Remove All” buttons. These buttons require a different logic than the case of a single time-series being added or removed. Presented below is the removeWindow() function:

function removeWindow(name, longTransitionBoolean, firstClick) {
    var transition = longTransitionBoolean ? 500 : 0;
    var idName = "#index" + name.toString();
    d3.select(idName)
        .transition()
        .style("opacity", 0.0)
        .duration(transition)
        .remove().each("end", function() {
            if (firstClick) {
                var displayedIndices = d3.selectAll(".index").data();
                if (!d3.selectAll(".index").empty()) {
                    y.domain([
                        d3.min(displayedIndices, function(c) {
                            return d3.min(c.values, function(v) {
                                return v.yvalue;
                            });
                        }),
                        d3.max(displayedIndices, function(c) {
                            return d3.max(c.values, function(v) {
                                return v.yvalue;
                            });
                        })
                    ]);
                    svg.select(".y.axis")
                        .call(yAxis);
                    d3.selectAll(".index").data().forEach(function(d) {
                        removeWindow(d.name, false, false);
                        addWindow(d.name, true, true, false);

                    });
                    addMouse();
                }
            }
        });

In each of these functions, the D3 transition function is called in order to make the addition and remove of the time-series lines smooth.
This concludes the posts concerning drawing multiple line plots with mouse interaction in D3.js. Hope this helps!

]]>
https://lpplmarketwatch.com/2015/07/30/d3-js-multiple-line-plot-part-6/feed/ 0
D3.js Multiple Line Plot – Part 5 https://lpplmarketwatch.com/2015/07/29/d3-js-multiple-line-plot-part-5/ https://lpplmarketwatch.com/2015/07/29/d3-js-multiple-line-plot-part-5/#respond Wed, 29 Jul 2015 01:23:44 +0000 https://lpplmarketwatch.com/?p=678 Working version of the plot, download here.

The D3 function, d3.mouse(), provides a convenient tool for tracking the movement of the mouse as the user interacts with the plot. The nearest time x-value is determined and used to display the corresponding y-values for each time-series displayed. The mousemove() function was initialized in part 1 as follows:

function mousemove() {
    var x0 = x.invert(d3.mouse(d3.select("#svgMain").node())[0]);
    var i = bisectDate(origData, x0, 1);
    d3.selectAll(".index").data().forEach(function(d) {
        var d0 = d.values[i - 1],
            d1 = d.values[i],
            p = x0 - d0.date > d1.date - x0 ? d1 : d0;
        var x_new = x(p.date),
            y_new = y(p.yvalue);
        svg.select("#focus" + d.name).attr("transform", "translate(" + x_new + "," + y_new + ")");
        svg.select("#numtext" + d.name).text(p.yvalue);
				svg.select("#datetext").text(dateDisplayFormat(p.date));
    });
}

The code in part 4 displayed where the mousemove() function is first called.

Next, add html and svg elements which draw circles and append text to the plot display at the location and value of the data points. These objects will follow the movement of the mouse. The location of the nearest Date will be fixed in the top center of the plot; its value will change accordingly as the mouse moves. Each time-series line currently displayed on the plot will need to have a “rect” appended to it, as shown below:

var addMouse = function() {
    focus = svg.selectAll(".index").append("g")
        .attr("id", function(d) {
            return "focus" + d.name;
        })
        .attr("class", function(d) {
            return "focus " + d.name;
        })
        .style("display", "none");
    focus.append("circle")
        .attr("id", function(d) {
            return "circle" + d.name;
        })
        .attr("r", 4.5);
    focus.append("text")
        .attr("id", function(d) {
            return "numtext" + d.name;
        })
        .attr("x", 9)
        .attr("dy", "-0.5em");
    svg.append("text")
        .attr("id", "datetext")
        .attr("x", width / 2);
    svg.selectAll(".index").append("rect")
        .attr("class", function(d) {
            return "overlay " + d.name;
        })
        .attr("width", width)
        .attr("height", height)
        .on("mouseover", function() {
            focus.style("display", null);
        })
        .on("mouseout", function() {
            focus.style("display", "none");
        })
        .on("mousemove", mousemove);
}

The circles that trace the multiple plot lines will appear and disappear as the mouse moves onto and off the plot. This is associated with the following CSS style code:

.overlay {
  fill: none;
  pointer-events: all;
}
.focus circle {
  fill: none;
  stroke: steelblue;
}
]]>
https://lpplmarketwatch.com/2015/07/29/d3-js-multiple-line-plot-part-5/feed/ 0
D3.js Multiple Line Plot – Part 4 https://lpplmarketwatch.com/2015/07/28/d3-js-multiple-line-plot-part-4/ https://lpplmarketwatch.com/2015/07/28/d3-js-multiple-line-plot-part-4/#respond Tue, 28 Jul 2015 00:47:31 +0000 https://lpplmarketwatch.com/?p=661 Working version of the plot, download here.

This post discusses the code responsible for creating an interactive legend for a multiple line plot — as seen on the Plot page of The Bubble Index. The legend will be located below the plot. In order to create an area to contain room for the legend’s text, the plot display must not fill the entire height of the svg object. As seen in part 1, the plot has a bottom margin of size 100 and a top margin of size 20. This allows space for a legend to span the area below the plot.

var maxPerRow = Math.ceil(indices.length / 2);
var legendSpace = width / maxPerRow;

The code assumes that two rows will be enough to display all the time-series labels, including an “Add All” and “Remove All” button.

The “Add All” feature will add every time-series contained in the original data to the plot. It is like a plot “reset.” Whatever is currently displayed will be removed. Thus, the y-axis will need to be re-scaled to fit the original range of data.

svg.append("text")
    .attr("x", 0)
    .attr("y", height + (margin.bottom / 2) + 15)
    .attr("class", "legend addAll")
    .style("fill", "red")
    .text("Add All")
    .on("click", function() {
        d3.selectAll(".index").data().forEach(function(d) {
            removeWindow(d.name, false, false);
        });
        y.domain([
            d3.min(indices, function(c) {
                return d3.min(c.values, function(v) {
                    return v.yvalue;
                });
            }),
            d3.max(indices, function(c) {
                return d3.max(c.values, function(v) {
                    return v.yvalue;
                });
            })
        ]);
        svg.select(".y.axis")
            .call(yAxis);
        indices.forEach(function(d, i) {
            var myID = "legend" + d.name;
            var selection = d3.select("#" + myID);
            if (selection.attr("class") == "legend-clicked") {
                selection.style("fill", color(d.name)).attr("class", "legend " + d.name);
                addWindow(d.name, true, false, true);
            }
        });
        addMouse();
    });

The “Remove All” feature will remove all time-series currently displayed in the plot. Note: while writing this, I noticed it may not be necessary to re-scale the y-axis.

svg.append("text")
    .attr("x", 0)
    .attr("y", height + (margin.bottom / 2) + 45)
    .attr("class", "legend removeAll")
    .style("fill", "red")
    .text("Remove All")
    .on("click", function() {
        indices.forEach(function(d, i) {
            var myID = "legend" + d.name;
            var selection = d3.select("#" + myID);
            selection.style("fill", "black").attr("class", "legend-clicked");
            removeWindow(d.name, true, false);
        });
        var displayedIndices = d3.selectAll(".index").data();
        y.domain([
            d3.min(displayedIndices, function(c) {
                return d3.min(c.values, function(v) {
                    return v.yvalue;
                });
            }),
            d3.max(displayedIndices, function(c) {
                return d3.max(c.values, function(v) {
                    return v.yvalue;
                });
            })
        ]);
        svg.select(".y.axis")
            .call(yAxis);
    });

At this point in the code, the function which creates a mouse-over interaction is called. The mouse-over function will be discussed in a later post.

addMouse();

Next, a legend entry for each time-series is appended to the bottom of the svg plot. Note: In the HTML header, there is a CSS style sheet which contains a class for a legend element which has been clicked-on.

.legend {
    font-size: 16px;
    font-weight: bold;
    text-anchor: middle;
    cursor:pointer;
} 

.legend-clicked {
    font-size: 16px;
    font-weight: bold;
    text-anchor: middle;
    text-decoration: line-through;
    cursor:pointer;
}

When the user clicks on a time-series label, that particular time-series will either be removed or added, depending on the current status of that time-series. For instance, if the “52 Day” label is black and has a strike-through style, it means the time-series has been removed from the plot. Clicking on this label will add the time-series back to the plot. However, in order for this to happen, the y-axis needs to be re-scaled and all the currently displayed lines will need to be re-drawn. The functions which re-scale and update the plot will be included in a future post.

indices.forEach(function(d, i) {
    svg.append("text")
        .attr("x", legendSpace + (i % maxPerRow) * legendSpace)
        .attr("y", (i & lt; maxPerRow) ? (height + (margin.bottom / 2) + 15) : (height + (margin.bottom / 2) + 45))
        .attr("class", "legend " + d.name)
        .attr("id", "legend" + d.name)
        .style("fill", color(d.name))
        .text(d.name + " Days")
        .on("click", function() {
            var selection = d3.select(this);
            if (selection.attr("class") == "legend-clicked") {
                selection.style("fill", color(d.name)).attr("class", "legend " + d.name);
                addWindow(d.name, false, false, true);
                addMouse();
            } else {
                selection.style("fill", "black").attr("class", "legend-clicked");
                removeWindow(d.name, true, true);
            }
        });
});
]]>
https://lpplmarketwatch.com/2015/07/28/d3-js-multiple-line-plot-part-4/feed/ 0
D3.js Multiple Line Plot – Part 3 https://lpplmarketwatch.com/2015/07/24/d3-js-multiple-line-plot-part-3/ https://lpplmarketwatch.com/2015/07/24/d3-js-multiple-line-plot-part-3/#respond Fri, 24 Jul 2015 00:53:57 +0000 https://lpplmarketwatch.com/?p=650 Working version of the plot, download here.

The data is now imported and loaded into memory. The next step is to attach each time-series to the DOM as an element. Before doing that, draw the initial axes.

Drawing the initial axes are easy with D3. Nothing too crazy, the y-axis has the text: “Relative Power.” However, note that the y-axis has been scaled (part 2) based on all the time-series. If a single time-series is removed, the y-axis should be re-scaled and re-drawn. The entire plot will also have to be re-drawn. Removing and added lines will be the subject of later posts.

    svg.append("g")
        .attr("class", "x axis")
        .attr("transform", "translate(0," + height + ")")
        .call(xAxis);

    svg.append("g")
        .attr("class", "y axis")
        .attr("transform", "translate(" + width + " ,0)")
        .call(yAxis)
        .append("text")
        .attr("y", 6)
        .attr("dy", "-.71em")
        .style("text-anchor", "end")
        .text("Relative Power");

Now the initial lines can be drawn on the plot. Since the initial state of the svg selection contains no child elements having the “.index” keyword, the D3 selection: svg.selectAll(".index") will return with an empty selection. Thus, the combination of the .data(indices) and .enter().append("g") D3 functions will append a “g” element for every map key in the index object. Each “g” element will have an “id” and “class” based on the individual time-series “name” attribute.

For each time-series object attached to the index selection, the .append("path") function will create a “path” element. This path will be drawn based on the values entry of the individual time-series map. The function(d) in the D3 callback of .attr() implicitly iterates over all key-pairs in the index selection and all key-pair in each time-series. The line and color functions were initialized in part 1.

    index = svg.selectAll(".index")
        .data(indices)
        .enter().append("g")
        .attr("id", function(d) {
            return "index" + d.name;
        })
        .attr("class", function(d) {
            return "index a" + d.name;
        });

    index.append("path")
        .attr("class", "line")
        .attr("d", function(d) {
            return line(d.values);
        })
        .style("stroke", function(d) {
            return color(d.name);
        });
]]>
https://lpplmarketwatch.com/2015/07/24/d3-js-multiple-line-plot-part-3/feed/ 0
D3.js Multiple Line Plot – Part 2 https://lpplmarketwatch.com/2015/07/22/d3-js-multiple-line-plot-part-2/ https://lpplmarketwatch.com/2015/07/22/d3-js-multiple-line-plot-part-2/#respond Wed, 22 Jul 2015 01:01:29 +0000 https://lpplmarketwatch.com/?p=640 Working version of the plot, download here.

As mentioned before, the following code is presented “as it is” in its current version. Some functions and variables are presented out of order.

The SVG variable is created by a D3 selection. The “plotarea” <div> is selected as the element where the SVG plot will be created. A “g” element is appended to the plot. This will allow the time-series to be grouped together as a single object.

var svg = d3.select("#plotarea").append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
    .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")")
    .attr("id", "svgMain");

The source data is of course the most important piece in the puzzle. In order to create an SVG plot containing multiple lines which each share the same set of dates, make sure that the data is formatted and aligned correctly. For instance, create a tab-separated file which contains a header entry for each column. And make sure each row contains an entry for each column. Thus, if no data is available for a specific date and column, place a value of 0, ensuring no gaps in the data. However, depending on the structure of the data, this may not work for all applications.

An example header for the tsv file: “date 52 Days 104 Days 153 Days 256 Days 512 Days”, where each “# Days” entry is separated by a tab.

Example:

date    52 Days    104 Days
2015-05-01    52.0    102.0
2015-05-02    54.0    104.0

D3 has a function called “d3.tsv” which easily requests and loads a tab-separated file into the browser memory.

Once this raw data has been loaded, the “d3.tsv” function enters a callback. This is where the data is parsed, turned into SVG objects, and loaded into a DOM element.

Since there are multiple time-series, there will be a separate map for each column. The “indices” variable contains the separate time-series as a key-value pair mapping in memory. Each column header (i.e. 52 Days) is the key for a time-series. The value entry of the “indices” map is itself another map object. This additional map object will have a set of key-value pairs between dates and numerical values. Thus, if there are 5 column headers (excluding the date column) then the “indices” object will have 5 keys. And, if there are 252 date rows, each map associated with each one of the 5 keys will have 252 date keys.

Once the data is stored as a map, the D3 “domain” functions — in combination with the D3 max and min functions — will determine the numerical and time-line ranges of ALL the time-series data.

d3.tsv("data-location_name.tsv", function(error, data) {
    color.domain(d3.keys(data[0]).filter(function(key) {
        return key !== "date";
    }));

    data.forEach(function(d) {
        d.date = parseDate(d.date);
    });

    indices = color.domain().map(function(name) {
        return {
            name: name.substring(0, name.indexOf(" Days")),
            values: data.map(function(d) {
                return {
                    date: d.date,
                    yvalue: +d[name]
                };
            })
        };
    });
    origData = data;
    x.domain(d3.extent(data, function(d) {
        return d.date;
    }));

    y.domain([
        d3.min(indices, function(c) {
            return d3.min(c.values, function(v) {
                return v.yvalue;
            });
        }),
        d3.max(indices, function(c) {
            return d3.max(c.values, function(v) {
                return v.yvalue;
            });
        })
    ]);
]]>
https://lpplmarketwatch.com/2015/07/22/d3-js-multiple-line-plot-part-2/feed/ 0
D3.js Multiple Line Plot – Part 1 https://lpplmarketwatch.com/2015/07/21/d3-multiple-line-plot-part-1/ https://lpplmarketwatch.com/2015/07/21/d3-multiple-line-plot-part-1/#respond Tue, 21 Jul 2015 01:46:08 +0000 https://lpplmarketwatch.com/?p=627 To help any one interested in creating “multi-line” or multiple line charts with D3.js, I will walk through the various steps required to create the D3 plots on The Bubble Index. There may be many places in my code which could be simplified and improved — I present the current version “as it is.”

Working version of the plot, download here.

The goal is to have the SVG plot fill the width of any screen which accesses the site. The HTML code contains a “plotarea” <div> which has its CSS width property set to: width = 100%. Note: this uses jQuery to select the <div>, but certainly there are other ways to select the <div>. Therefore, no matter how wide the screen on the device, the “areawidth” variable will store that information. This code setup means that the “plotarea” will only resize itself after a page resize and refresh F5, which is ok for my purposes. The aspect ratio and margins are set to visually appealing values.

The next lines contain the following useful functions:

  • D3 date parser to extract date objects from the time-series data
  • D3 scale functions to ensure that the time-series data for each time-series is always scaled with the SVG in the x and y directions
  • D3 line function to draw the lines on the SVG — “basis” interpolation method
  • D3 bisector function to be used in coordination with mouse-over events to determine the nearest “x” value of the current position of the cursor
var index;
var origData;
var indices;
var focus;
var margin = {
        top: 20,
        right: 80,
        bottom: 100,
        left: 50
    },
    aspect = 1.92,
    areawidth = $("#plotarea").width(),
    width = areawidth - margin.left - margin.right,
    height = Math.round(areawidth / aspect) - margin.top - margin.bottom;

var parseDate = d3.time.format("%Y-%m-%d").parse;

var x = d3.time.scale()
    .range([0, width]);

var y = d3.scale.linear()
    .range([height, 0]);

var color = d3.scale.category10();

var dateDisplayFormat = d3.time.format("%Y-%m-%d");

var xAxis = d3.svg.axis()
    .scale(x)
    .orient("bottom");

var yAxis = d3.svg.axis()
    .scale(y)
    .orient("right");

var line = d3.svg.line()
    .interpolate("basis")
    .x(function(d) {
        return x(d.date);
    })
    .y(function(d) {
        return y(d.yvalue);
    });

var bisectDate = d3.bisector(function(d) {
    return d.date;
}).left;
]]>
https://lpplmarketwatch.com/2015/07/21/d3-multiple-line-plot-part-1/feed/ 0
Website Update https://lpplmarketwatch.com/2015/05/31/website-update/ https://lpplmarketwatch.com/2015/05/31/website-update/#respond Sun, 31 May 2015 13:24:05 +0000 https://lpplmarketwatch.com/?p=608 In the next couple of days I will have the code/scripts ready to batch create 3D contour maps in Three.js for all the thousands of currently monitored markets and stocks. They will then be updated on a weekly basis.

Also, with the help of D3.js in Action by Elijah Meeks I will change and improve the current D3 data graph. If there are any suggestions please leave a comment. My plan is to have the data available for 15 or more windows via a legend/check box/drop-down menu; the user will be able to select/choose which windows he/she wants plotted on the D3 graph. For examples and potential ideas: D3 Examples. I think this one would be good for the site: UK Temperature History.

]]>
https://lpplmarketwatch.com/2015/05/31/website-update/feed/ 0
Hierarchical Edge Bundles: Visualization of Adjacency Relations in Hierarchical Data by Danny Holten https://lpplmarketwatch.com/2014/05/31/hierarchical-edge-bundles-visualization-of-adjacency-relations-in-hierarchical-data-by-danny-holten/ https://lpplmarketwatch.com/2014/05/31/hierarchical-edge-bundles-visualization-of-adjacency-relations-in-hierarchical-data-by-danny-holten/#respond Sat, 31 May 2014 22:18:00 +0000 https://lpplmarketwatch.com/?p=27 A thought inspiring article, entitled Hierarchical Edge Bundles: Visualization of Adjacency Relations in Hierarchical Data, by Danny Holten, would provide a possible way to visualize the flow of capital via hierarchical financial networks. The hierarchy would consist of governments, banks, exchanges, and traders. With such a visual tool, one may be able to identify connections in the network which are vital to the entire system. The more densely connected regions of the network would represent areas for officials to focus their efforts in either preventing a crash or ensuring a “safe landing” for an inflated economy.

For some reason, I found that the following picture would be perfect for visualizing the hierarchy of big institutions. The bigger bubbles would represent the larger institutions.

]]>
https://lpplmarketwatch.com/2014/05/31/hierarchical-edge-bundles-visualization-of-adjacency-relations-in-hierarchical-data-by-danny-holten/feed/ 0
Source Code Release https://lpplmarketwatch.com/2014/05/26/source-code-release/ https://lpplmarketwatch.com/2014/05/26/source-code-release/#respond Mon, 26 May 2014 21:22:00 +0000 https://lpplmarketwatch.com/?p=28 The source code and a downloadable release can be found here: CodePlex: The Bubble Index.

]]>
https://lpplmarketwatch.com/2014/05/26/source-code-release/feed/ 0
Potential New Site https://lpplmarketwatch.com/2014/04/28/potential-new-site/ https://lpplmarketwatch.com/2014/04/28/potential-new-site/#respond Mon, 28 Apr 2014 03:48:00 +0000 https://lpplmarketwatch.com/?p=33 In previous posts I have mentioned, with few details, a potential way to display the state of the trading network. Over the past few weeks I have been playing with the idea and progress is being made. The site, Particle Markets, will be the center of these new ideas to publicly and freely display the state of trading networks.

Currently the site displays a simple, 2D Ising type model, progressing through time. There are 176,400 total traders, and they all start with Long positions. As time progresses many of the traders sell and become either neutral or short. All traders are long/short 1 share. The summation of their numbers after each time step determines the change in price of the traded security.

Short positions: RED
Long positions: GREEN
Neutral: BLUE

This is a simple model. Future models will incorporate more details. I hope to be able to use actual data one day and display daily changes in the actual human network; in the same way a meteorologist shows viewers his Doppler radar to warn of storms, Particle Markets will be able to show investors the “market weather.”

The concept applies mostly to the stock market, since a trader holds a contract which has no “delivery date.” I hope to apply the same ideas to commodity markets.

Future Update Ideas:

]]>
https://lpplmarketwatch.com/2014/04/28/potential-new-site/feed/ 0