ChartDirector 6.0 (Ruby Edition)

Interactive Financial Chart




This example demonstrates a full-featured financial chart in which the user can select the time ranges, technical indicators, and various chart options.

The default view "index.rhtml" is basically just a standard HTML Form. When the "Update Chart" button is pressed, instead of posting to the server, a client side Javascript function is activated. This function combines all Form elements into a query string, appends it to the charting URL "<%= url_for(:action => 'getchart') %>" and uses it to update the <IMG> tag.

The "getchart" action draws the finance chart according to the query parameters, which reflects user selections, and outputs the chart to the <IMG> tag.

The followings are the main functions used in the "getchart" action.

Source Code Listing

[Ruby On Rails Version - Controller] app/controllers/financedemo_controller.rb
require("financechart")

class FinancedemoController < ApplicationController

    #
    # Create a finance chart based on user selections, which are encoded as query parameters. This
    # code is designed to work with the financedemo HTML form.
    #

private

    def initMembers()
        # The timeStamps, volume, high, low, open and close data
        #
        # ** NOTE ** : This sample code is written assuming the time stamps are in ChartDirector
        # chartTime format. It is because this format supports dates before 1970 (which may be
        # needed in some long term charts). See the ChartDirector documentation on chartTime for
        # details. When you retrieve the time stamps from your database, please remember to convert
        # them to chartTime.
        @timeStamps = nil
        @volData = nil
        @highData = nil
        @lowData = nil
        @openData = nil
        @closeData = nil

        # An extra data series to compare with the close data
        @compareData = nil

        # The resolution of the data in seconds. 1 day = 86400 seconds.
        @resolution = 86400
    end

    #/ <summary>
    #/ Get the timeStamps, highData, lowData, openData, closeData and volData.
    #/ </summary>
    #/ <param name="ticker">The ticker symbol for the data series.</param>
    #/ <param name="startDate">The starting date/time for the data series.</param>
    #/ <param name="endDate">The ending date/time for the data series.</param>
    #/ <param name="durationInDays">The number of trading days to get.</param>
    #/ <param name="extraPoints">The extra leading data points needed in order to
    #/ compute moving averages.</param>
    #/ <returns>True if successfully obtain the data, otherwise false.</returns>
    def getData(ticker, startDate, endDate, durationInDays, extraPoints)
        # This method should return false if the ticker symbol is invalid. In this sample code, as
        # we are using a random number generator for the data, all ticker symbol is allowed, but we
        # still assumed an empty symbol is invalid.
        if ticker == ""
            return false
        end

        # In this demo, we can get 15 min, daily, weekly or monthly data depending on the time
        # range.
        @resolution = 86400
        if durationInDays <= 10
            # 10 days or less, we assume 15 minute data points are available
            @resolution = 900

            # We need to adjust the startDate backwards for the extraPoints. We assume 6.5 hours
            # trading time per day, and 5 trading days per week.
            dataPointsPerDay = 6.5 * 3600 / @resolution
            adjustedStartDate = startDate - startDate % 86400 - (
                extraPoints / dataPointsPerDay * 7 / 5 + 0.9999999).to_i * 86400 - 2 * 86400

            # Get the required 15 min data
            get15MinData(ticker, adjustedStartDate, endDate)

        elsif durationInDays >= 4.5 * 360
            # 4 years or more - use monthly data points.
            @resolution = 30 * 86400

            # Adjust startDate backwards to cater for extraPoints
            yMD = ChartDirector::getChartYMD(startDate)
            currentMonth = (yMD / 100).to_i % 100 - extraPoints
            currentYear = (yMD / 10000).to_i
            while currentMonth < 1
                currentYear = currentYear - 1
                currentMonth = currentMonth + 12
            end
            adjustedStartDate = ChartDirector::chartTime(currentYear, currentMonth, 1)

            # Get the required monthly data
            getMonthlyData(ticker, adjustedStartDate, endDate)

        elsif durationInDays >= 1.5 * 360
            # 1 year or more - use weekly points.
            @resolution = 7 * 86400

            # Adjust startDate backwards to cater for extraPoints
            adjustedStartDate = startDate - extraPoints * 7 * 86400 - 6 * 86400

            # Get the required weekly data
            getWeeklyData(ticker, adjustedStartDate, endDate)

        else
            # Default - use daily points
            @resolution = 86400

            # Adjust startDate backwards to cater for extraPoints. We multiply the days by 7/5 as we
            # assume 1 week has 5 trading days.
            adjustedStartDate = startDate - startDate % 86400 - ((extraPoints * 7 + 4) / 5
                ).to_i * 86400 - 2 * 86400

            # Get the required daily data
            getDailyData(ticker, adjustedStartDate, endDate)
        end

        return true
    end

    #/ <summary>
    #/ Get 15 minutes data series for timeStamps, highData, lowData, openData, closeData
    #/ and volData.
    #/ </summary>
    #/ <param name="ticker">The ticker symbol for the data series.</param>
    #/ <param name="startDate">The starting date/time for the data series.</param>
    #/ <param name="endDate">The ending date/time for the data series.</param>
    def get15MinData(ticker, startDate, endDate)
        #
        # In this demo, we use a random number generator to generate the data. In practice, you may
        # get the data from a database or by other means. If you do not have 15 minute data, you may
        # modify the "drawChart" method below to not using 15 minute data.
        #
        generateRandomData(ticker, startDate, endDate, 900)
    end

    #/ <summary>
    #/ Get daily data series for timeStamps, highData, lowData, openData, closeData
    #/ and volData.
    #/ </summary>
    #/ <param name="ticker">The ticker symbol for the data series.</param>
    #/ <param name="startDate">The starting date/time for the data series.</param>
    #/ <param name="endDate">The ending date/time for the data series.</param>
    def getDailyData(ticker, startDate, endDate)
        #
        # In this demo, we use a random number generator to generate the data. In practice, you may
        # get the data from a database or by other means.
        #
        generateRandomData(ticker, startDate, endDate, 86400)
    end

    #/ <summary>
    #/ Get weekly data series for timeStamps, highData, lowData, openData, closeData
    #/ and volData.
    #/ </summary>
    #/ <param name="ticker">The ticker symbol for the data series.</param>
    #/ <param name="startDate">The starting date/time for the data series.</param>
    #/ <param name="endDate">The ending date/time for the data series.</param>
    def getWeeklyData(ticker, startDate, endDate)
        #
        # If you do not have weekly data, you may call "getDailyData(startDate, endDate)" to get
        # daily data, then call "convertDailyToWeeklyData()" to convert to weekly data.
        #
        generateRandomData(ticker, startDate, endDate, 86400 * 7)
    end

    #/ <summary>
    #/ Get monthly data series for timeStamps, highData, lowData, openData, closeData
    #/ and volData.
    #/ </summary>
    #/ <param name="ticker">The ticker symbol for the data series.</param>
    #/ <param name="startDate">The starting date/time for the data series.</param>
    #/ <param name="endDate">The ending date/time for the data series.</param>
    def getMonthlyData(ticker, startDate, endDate)
        #
        # If you do not have weekly data, you may call "getDailyData(startDate, endDate)" to get
        # daily data, then call "convertDailyToMonthlyData()" to convert to monthly data.
        #
        generateRandomData(ticker, startDate, endDate, 86400 * 30)
    end

    #/ <summary>
    #/ A random number generator designed to generate realistic financial data.
    #/ </summary>
    #/ <param name="ticker">The ticker symbol for the data series.</param>
    #/ <param name="startDate">The starting date/time for the data series.</param>
    #/ <param name="endDate">The ending date/time for the data series.</param>
    #/ <param name="resolution">The period of the data series.</param>
    def generateRandomData(ticker, startDate, endDate, interval)
        db = ChartDirector::FinanceSimulator.new(ticker, startDate, endDate, interval)
        @timeStamps = db.getTimeStamps()
        @highData = db.getHighData()
        @lowData = db.getLowData()
        @openData = db.getOpenData()
        @closeData = db.getCloseData()
        @volData = db.getVolData()
    end

    #/ <summary>
    #/ A utility to convert daily to weekly data.
    #/ </summary>
    def convertDailyToWeeklyData()
        aggregateData(ChartDirector::ArrayMath.new(@timeStamps).selectStartOfWeek())
    end

    #/ <summary>
    #/ A utility to convert daily to monthly data.
    #/ </summary>
    def convertDailyToMonthlyData()
        aggregateData(ChartDirector::ArrayMath.new(@timeStamps).selectStartOfMonth())
    end

    #/ <summary>
    #/ An internal method used to aggregate daily data.
    #/ </summary>
    def aggregateData(aggregator)
        @timeStamps = aggregator.aggregate(@timeStamps, ChartDirector::AggregateFirst)
        @highData = aggregator.aggregate(@highData, ChartDirector::AggregateMax)
        @lowData = aggregator.aggregate(@lowData, ChartDirector::AggregateMin)
        @openData = aggregator.aggregate(@openData, ChartDirector::AggregateFirst)
        @closeData = aggregator.aggregate(@closeData, ChartDirector::AggregateLast)
        @volData = aggregator.aggregate(@volData, ChartDirector::AggregateSum)
    end

    #/ <summary>
    #/ Create a financial chart according to user selections. The user selections are
    #/ encoded in the query parameters.
    #/ </summary>
    def drawChart()
        # In this demo, we just assume we plot up to the latest time. So end date is now.
        endDate = Time.new

        # ChartDirector supports date/time as both Ruby Time objects and in ChartDirector::chartTime
        # format. Because Ruby Time objects cannot support dates before year 1970 (which may be
        # needed in stock charts), so in this demo, we use the ChartDirector::chartTime format.
        endDate = ChartDirector::chartTime2(endDate.to_i)

        # If the trading day has not yet started (before 9:30am), or if the end date is on on Sat or
        # Sun, we set the end date to 4:00pm of the last trading day
        while (endDate % 86400 < 9 * 3600 + 30 * 60) || (ChartDirector::getChartWeekDay(endDate
            ) == 0) || (ChartDirector::getChartWeekDay(endDate) == 6)
            endDate = endDate - endDate % 86400 - 86400 + 16 * 3600
        end

        # The duration selected by the user
        durationInDays = (params["TimeRange"]).to_i

        # Compute the start date by subtracting the duration from the end date.
        startDate = endDate
        if durationInDays >= 30
            # More or equal to 30 days - so we use months as the unit
            yMD = ChartDirector::getChartYMD(endDate)
            startMonth = (yMD / 100).to_i % 100 - (durationInDays / 30).to_i
            startYear = (yMD / 10000).to_i
            while startMonth < 1
                startYear = startYear - 1
                startMonth = startMonth + 12
            end
            startDate = ChartDirector::chartTime(startYear, startMonth, 1)
        else
            # Less than 30 days - use day as the unit. The starting point of the axis is always at
            # the start of the day (9:30am). Note that we use trading days, so we skip Sat and Sun
            # in counting the days.
            startDate = endDate - endDate % 86400 + 9 * 3600 + 30 * 60
            1.upto(durationInDays - 1) do |i|
                if ChartDirector::getChartWeekDay(startDate) == 1
                    startDate = startDate - 3 * 86400
                else
                    startDate = startDate - 86400
                end
            end
        end

        # The moving average periods selected by the user.
        avgPeriod1 = 0
        avgPeriod1 = params["movAvg1"].to_i
        avgPeriod2 = 0
        avgPeriod2 = params["movAvg2"].to_i

        if avgPeriod1 < 0
            avgPeriod1 = 0
        elsif avgPeriod1 > 300
            avgPeriod1 = 300
        end

        if avgPeriod2 < 0
            avgPeriod2 = 0
        elsif avgPeriod2 > 300
            avgPeriod2 = 300
        end

        # We need extra leading data points in order to compute moving averages.
        extraPoints = 20
        if avgPeriod1 > extraPoints
            extraPoints = avgPeriod1
        end
        if avgPeriod2 > extraPoints
            extraPoints = avgPeriod2
        end

        # Get the data series to compare with, if any.
        compareKey = (params["CompareWith"] || "").strip
        @compareData = nil
        if getData(compareKey, startDate, endDate, durationInDays, extraPoints)
              @compareData = @closeData
        end

        # The data series we want to get.
        tickerKey = (params["TickerSymbol"] || "").strip
        if !getData(tickerKey, startDate, endDate, durationInDays, extraPoints)
            return errMsg("Please enter a valid ticker symbol")
        end

        # We now confirm the actual number of extra points (data points that are before the start
        # date) as inferred using actual data from the database.
        extraPoints = @timeStamps.length
        0.upto(@timeStamps.length - 1) do |i|
            if @timeStamps[i] >= startDate
                extraPoints = i
                break
            end
        end

        # Check if there is any valid data
        if extraPoints >= @timeStamps.length
            # No data - just display the no data message.
            return errMsg("No data available for the specified time period")
        end

        # In some finance chart presentation style, even if the data for the latest day is not fully
        # available, the axis for the entire day will still be drawn, where no data will appear near
        # the end of the axis.
        if @resolution < 86400
            # Add extra points to the axis until it reaches the end of the day. The end of day is
            # assumed to be 16:00 (it depends on the stock exchange).
            lastTime = @timeStamps[@timeStamps.length - 1]
            extraTrailingPoints = ((16 * 3600 - lastTime % 86400) / @resolution).to_i
            0.upto(extraTrailingPoints - 1) do |i|
                @timeStamps.push(lastTime + @resolution * (i + 1))
            end
        end

        #
        # At this stage, all data are available. We can draw the chart as according to user input.
        #

        #
        # Determine the chart size. In this demo, user can select 4 different chart sizes. Default
        # is the large chart size.
        #
        width = 780
        mainHeight = 255
        indicatorHeight = 80

        size = params["ChartSize"]
        if size == "S"
            # Small chart size
            width = 450
            mainHeight = 160
            indicatorHeight = 60
        elsif size == "M"
            # Medium chart size
            width = 620
            mainHeight = 215
            indicatorHeight = 70
        elsif size == "H"
            # Huge chart size
            width = 1000
            mainHeight = 320
            indicatorHeight = 90
        end

        # Create the chart object using the selected size
        m = ChartDirector::FinanceChart.new(width)

        # Set the data into the chart object
        m.setData(@timeStamps, @highData, @lowData, @openData, @closeData, @volData, extraPoints)

        #
        # We configure the title of the chart. In this demo chart design, we put the company name as
        # the top line of the title with left alignment.
        #
        m.addPlotAreaTitle(ChartDirector::TopLeft, tickerKey)

        # We displays the current date as well as the data resolution on the next line.
        resolutionText = ""
        if @resolution == 30 * 86400
            resolutionText = "Monthly"
        elsif @resolution == 7 * 86400
            resolutionText = "Weekly"
        elsif @resolution == 86400
            resolutionText = "Daily"
        elsif @resolution == 900
            resolutionText = "15-min"
        end

        m.addPlotAreaTitle(ChartDirector::BottomLeft, sprintf(
            "<*font=arial.ttf,size=8*>%s - %s chart", m.formatValue(Time.new, "mmm dd, yyyy"),
            resolutionText))

        # A copyright message at the bottom left corner the title area
        m.addPlotAreaTitle(ChartDirector::BottomRight,
            "<*font=arial.ttf,size=8*>(c) Advanced Software Engineering")

        #
        # Add the first techical indicator according. In this demo, we draw the first indicator on
        # top of the main chart.
        #
        addIndicator(m, params["Indicator1"], indicatorHeight)

        #
        # Add the main chart
        #
        m.addMainChart(mainHeight)

        #
        # Set log or linear scale according to user preference
        #
        if params["LogScale"] == "1"
            m.setLogScale(true)
        end

        #
        # Set axis labels to show data values or percentage change to user preference
        #
        if params["PercentageScale"] == "1"
            m.setPercentageAxis()
        end

        #
        # Draw any price line the user has selected
        #
        mainType = params["ChartType"]
        if mainType == "Close"
            m.addCloseLine(0x000040)
        elsif mainType == "TP"
            m.addTypicalPrice(0x000040)
        elsif mainType == "WC"
            m.addWeightedClose(0x000040)
        elsif mainType == "Median"
            m.addMedianPrice(0x000040)
        end

        #
        # Add comparison line if there is data for comparison
        #
        if @compareData != nil
            if @compareData.length > extraPoints
                m.addComparison(@compareData, 0x0000ff, compareKey)
            end
        end

        #
        # Add moving average lines.
        #
        addMovingAvg(m, params["avgType1"], avgPeriod1, 0x663300)
        addMovingAvg(m, params["avgType2"], avgPeriod2, 0x9900ff)

        #
        # Draw candlesticks or OHLC symbols if the user has selected them.
        #
        if mainType == "CandleStick"
            m.addCandleStick(0x33ff33, 0xff3333)
        elsif mainType == "OHLC"
            m.addHLOC(0x008800, 0xcc0000)
        end

        #
        # Add parabolic SAR if necessary
        #
        if params["ParabolicSAR"] == "1"
            m.addParabolicSAR(0.02, 0.02, 0.2, ChartDirector::DiamondShape, 5, 0x008800, 0x000000)
        end

        #
        # Add price band/channel/envelop to the chart according to user selection
        #
        bandType = params["Band"]
        if bandType == "BB"
            m.addBollingerBand(20, 2, 0x9999ff, 0xc06666ff)
        elsif bandType == "DC"
            m.addDonchianChannel(20, 0x9999ff, 0xc06666ff)
        elsif bandType == "Envelop"
            m.addEnvelop(20, 0.1, 0x9999ff, 0xc06666ff)
        end

        #
        # Add volume bars to the main chart if necessary
        #
        if params["Volume"] == "1"
            m.addVolBars(indicatorHeight, 0x99ff99, 0xff9999, 0xc0c0c0)
        end

        #
        # Add additional indicators as according to user selection.
        #
        addIndicator(m, params["Indicator2"], indicatorHeight)
        addIndicator(m, params["Indicator3"], indicatorHeight)
        addIndicator(m, params["Indicator4"], indicatorHeight)

        return m
    end

    #/ <summary>
    #/ Add a moving average line to the FinanceChart object.
    #/ </summary>
    #/ <param name="m">The FinanceChart object to add the line to.</param>
    #/ <param name="avgType">The moving average type (SMA/EMA/TMA/WMA).</param>
    #/ <param name="avgPeriod">The moving average period.</param>
    #/ <param name="color">The color of the line.</param>
    #/ <returns>The LineLayer object representing line layer created.</returns>
    def addMovingAvg(m, avgType, avgPeriod, color)
        if avgPeriod > 1
            if avgType == "SMA"
                return m.addSimpleMovingAvg(avgPeriod, color)
            elsif avgType == "EMA"
                return m.addExpMovingAvg(avgPeriod, color)
            elsif avgType == "TMA"
                return m.addTriMovingAvg(avgPeriod, color)
            elsif avgType == "WMA"
                return m.addWeightedMovingAvg(avgPeriod, color)
            end
        end
        return nil
    end

    #/ <summary>
    #/ Add an indicator chart to the FinanceChart object. In this demo example, the
    #/ indicator parameters (such as the period used to compute RSI, colors of the lines,
    #/ etc.) are hard coded to commonly used values. You are welcome to design a more
    #/ complex user interface to allow users to set the parameters.
    #/ </summary>
    #/ <param name="m">The FinanceChart object to add the line to.</param>
    #/ <param name="indicator">The selected indicator.</param>
    #/ <param name="height">Height of the chart in pixels</param>
    #/ <returns>The XYChart object representing indicator chart.</returns>
    def addIndicator(m, indicator, height)
        if indicator == "RSI"
            return m.addRSI(height, 14, 0x800080, 20, 0xff6666, 0x6666ff)
        elsif indicator == "StochRSI"
            return m.addStochRSI(height, 14, 0x800080, 30, 0xff6666, 0x6666ff)
        elsif indicator == "MACD"
            return m.addMACD(height, 26, 12, 9, 0x0000ff, 0xff00ff, 0x008000)
        elsif indicator == "FStoch"
            return m.addFastStochastic(height, 14, 3, 0x006060, 0x606000)
        elsif indicator == "SStoch"
            return m.addSlowStochastic(height, 14, 3, 0x006060, 0x606000)
        elsif indicator == "ATR"
            return m.addATR(height, 14, 0x808080, 0x0000ff)
        elsif indicator == "ADX"
            return m.addADX(height, 14, 0x008000, 0x800000, 0x000080)
        elsif indicator == "DCW"
            return m.addDonchianWidth(height, 20, 0x0000ff)
        elsif indicator == "BBW"
            return m.addBollingerWidth(height, 20, 2, 0x0000ff)
        elsif indicator == "DPO"
            return m.addDPO(height, 20, 0x0000ff)
        elsif indicator == "PVT"
            return m.addPVT(height, 0x0000ff)
        elsif indicator == "Momentum"
            return m.addMomentum(height, 12, 0x0000ff)
        elsif indicator == "Performance"
            return m.addPerformance(height, 0x0000ff)
        elsif indicator == "ROC"
            return m.addROC(height, 12, 0x0000ff)
        elsif indicator == "OBV"
            return m.addOBV(height, 0x0000ff)
        elsif indicator == "AccDist"
            return m.addAccDist(height, 0x0000ff)
        elsif indicator == "CLV"
            return m.addCLV(height, 0x0000ff)
        elsif indicator == "WilliamR"
            return m.addWilliamR(height, 14, 0x800080, 30, 0xff6666, 0x6666ff)
        elsif indicator == "Aroon"
            return m.addAroon(height, 14, 0x339933, 0x333399)
        elsif indicator == "AroonOsc"
            return m.addAroonOsc(height, 14, 0x0000ff)
        elsif indicator == "CCI"
            return m.addCCI(height, 20, 0x800080, 100, 0xff6666, 0x6666ff)
        elsif indicator == "EMV"
            return m.addEaseOfMovement(height, 9, 0x006060, 0x606000)
        elsif indicator == "MDX"
            return m.addMassIndex(height, 0x800080, 0xff6666, 0x6666ff)
        elsif indicator == "CVolatility"
            return m.addChaikinVolatility(height, 10, 10, 0x0000ff)
        elsif indicator == "COscillator"
            return m.addChaikinOscillator(height, 0x0000ff)
        elsif indicator == "CMF"
            return m.addChaikinMoneyFlow(height, 21, 0x008000)
        elsif indicator == "NVI"
            return m.addNVI(height, 255, 0x0000ff, 0x883333)
        elsif indicator == "PVI"
            return m.addPVI(height, 255, 0x0000ff, 0x883333)
        elsif indicator == "MFI"
            return m.addMFI(height, 14, 0x800080, 30, 0xff6666, 0x6666ff)
        elsif indicator == "PVO"
            return m.addPVO(height, 26, 12, 9, 0x0000ff, 0xff00ff, 0x008000)
        elsif indicator == "PPO"
            return m.addPPO(height, 26, 12, 9, 0x0000ff, 0xff00ff, 0x008000)
        elsif indicator == "UO"
            return m.addUltimateOscillator(height, 7, 14, 28, 0x800080, 20, 0xff6666, 0x6666ff)
        elsif indicator == "Vol"
            return m.addVolIndicator(height, 0x99ff99, 0xff9999, 0xc0c0c0)
        elsif indicator == "TRIX"
            return m.addTRIX(height, 12, 0x0000ff)
        end
        return nil
    end

    #/ <summary>
    #/ Creates a dummy chart to show an error message.
    #/ </summary>
    #/ <param name="msg">The error message.
    #/ <returns>The BaseChart object containing the error message.</returns>
    def errMsg(msg)
        m = ChartDirector::MultiChart.new(400, 200)
        m.addTitle2(ChartDirector::Center, msg, "arial.ttf", 10).setMaxWidth(m.getWidth())
        return m
    end

public

    def getchart()
        initMembers()

        # create the finance chart
        c = drawChart()

        # Output the chart
        send_data(c.makeChart2(ChartDirector::PNG), :type => "image/png", :disposition => "inline")

    end

end

[Ruby On Rails Version - View] app/views/financedemo/index.html.erb
<html>
<head>
<title>ChartDirector Financial Chart Demonstration</title>
<style>
.inputtitle {font-size:11px; margin:10px 5px; font-family:verdana}
.input {font-size:11px; font-family:verdana}
</style>
<script language="Javascript">

//Cross browser method to get an object
function getObject(id)
{
    if (document.getElementById)
        //IE 5.x or NS 6.x or above
        return document.getElementById(id);
    else if (document.all)
        //IE 4.x
        return document.all[id];
    else
        //Netscape 4.x
        return document[id];
}

//Update the chart according to user selection
function updateChart()
{
    //
    //we encode the values of all form elements as query parameters
    //
    var elements = getObject("Form1").elements;
    var url = "<%= url_for(:action => 'getchart') %>" + "?";
    for (var i = 0; i < elements.length; ++i)
    {
        var e = elements[i];
        if (e.type == "checkbox")
            url = url + e.id + "=" + (e.checked ? "1" : "0") + "&";
        else
            url = url + e.id + "=" + escape(e.value) + "&";
    }

    //Now we update the URL of the image to update the chart
    getObject("ChartImage").src = url;
}
</script>
</head>
<body style="margin:0px" onLoad="updateChart();">
<table cellspacing="0" cellpadding="0" border="0">
    <tr>
        <td align="right" colspan="2" style="background:#000088">
            <div style="padding-right:3px; padding-bottom:2px; font-family:arial; font-size:10pt; font-weight:bold; font-style:italic">
                <a style="color:#ffff00; text-decoration:none" href="http://www.advsofteng.com">
                    Advanced Software Engineering
                </a>
            </div>
        </td>
    </tr>
    <tr valign="top">
        <td style="width:150px; background:#bbddff">
            <form id="Form1" action="javascript:updateChart()">
            <div class="inputtitle">
                <b>Ticker Symbol</b><br />
                <input id="TickerSymbol" name="TickerSymbol" class="input" style="width:140px;" value="ASE.SYMBOL">
            </div>
            <div class="inputtitle">
                <b>Compare With</b><br />
                <input id="CompareWith" name="CompareWith" class="input" style="width:140px;" value="">
            </div>
            <div class="inputtitle">
                <b>Time Period</b><br />
                <select id="TimeRange" name="TimeRange" class="input" style="width:140px;">
                    <option value="1">1 day</option>
                    <option value="2">2 days</option>
                    <option value="5">5 days</option>
                    <option value="10">10 days</option>
                    <option value="30">1 month</option>
                    <option value="60">2 months</option>
                    <option value="90">3 months</option>
                    <option value="180" selected>6 months</option>
                    <option value="360">1 year</option>
                    <option value="720">2 years</option>
                    <option value="1080">3 years</option>
                    <option value="1440">4 years</option>
                    <option value="1800">5 years</option>
                    <option value="3600">10 years</option>
                </select>
            </div>
            <div class="inputtitle">
                <b>Chart Size</b><br />
                <select id="ChartSize" name="ChartSize" class="input" style="width:140px;">
                    <option value="S">Small</option>
                    <option value="M">Medium</option>
                    <option value="L" selected>Large</option>
                    <option value="H">Huge</option>
                </select><br />
            </div>
            <div class="inputtitle">
                <input type="checkbox" id="Volume" name="Volume" checked><label for="Volume">Show Volume Bars</label><br />
                <input type="checkbox" id="ParabolicSAR" name="ParabolicSAR"><label for="ParabolicSAR">Parabolic SAR</label><br />
                <input type="checkbox" id="LogScale" name="LogScale"><label for="LogScale">Log Scale</label><br />
                <input type="checkbox" id="PercentageScale" name="PercentageScale"><label for="PercentageScale">Percentage Scale</label><br />
            </div>
            <div class="inputtitle">
                <b>Chart Type</b><br />
                <select id="ChartType" name="ChartType" class="input" style="width:140px;">
                    <option value="None">None</option>
                    <option value="CandleStick" selected>CandleStick</option>
                    <option value="Close">Closing Price</option>
                    <option value="Median">Median Price</option>
                    <option value="OHLC">OHLC</option>
                    <option value="TP">Typical Price</option>
                    <option value="WC">Weighted Close</option>
                </select>
            </div>
            <div class="inputtitle">
                <b>Price Band</b><br />
                <select id="Band" name="Band" class="input" style="width:140px;">
                    <option value="None">None</option>
                    <option value="BB" selected>Bollinger Band</option>
                    <option value="DC">Donchian Channel</option>
                    <option value="Envelop">Envelop (SMA 20 +/- 10%)</option>
                </select>
            </div>
            <div class="inputtitle">
                <b>Moving Averages</b><br />
                <nobr><select id="avgType1" name="avgType1" class="input" style="width:105px;">
                    <option value="None">None</option>
                    <option value="SMA" selected>Simple</option>
                    <option value="EMA">Exponential</option>
                    <option value="TMA">Triangular</option>
                    <option value="WMA">Weighted</option>
                </select>
                <input id="movAvg1" name="movAvg1" class="input" style="width:30px;" value="10"></nobr><br />
                <nobr><select id="avgType2" name="avgType2" class="input" style="width:105px;">
                    <option value="None">None</option>
                    <option value="SMA" selected>Simple</option>
                    <option value="EMA">Exponential</option>
                    <option value="TMA">Triangular</option>
                    <option value="WMA">Weighted</option>
                </select>
                <input id="movAvg2" name="movAvg2" class="input" style="width:30px;" value="25"></nobr><br />
            </div>
            <div class="inputtitle">
                <b>Technical Indicators</b><br />
                <select id="Indicator1" name="Indicator1" class="input" style="width:140px;">
                    <option value="None">None</option>
                    <option value="AccDist">Accumulation/Distribution</option>
                    <option value="AroonOsc">Aroon Oscillator</option>
                    <option value="Aroon">Aroon Up/Down</option>
                    <option value="ADX">Avg Directional Index</option>
                    <option value="ATR">Avg True Range</option>
                    <option value="BBW">Bollinger Band Width</option>
                    <option value="CMF">Chaikin Money Flow</option>
                    <option value="COscillator">Chaikin Oscillator</option>
                    <option value="CVolatility">Chaikin Volatility</option>
                    <option value="CLV">Close Location Value</option>
                    <option value="CCI">Commodity Channel Index</option>
                    <option value="DPO">Detrended Price Osc</option>
                    <option value="DCW">Donchian Channel Width</option>
                    <option value="EMV">Ease of Movement</option>
                    <option value="FStoch">Fast Stochastic</option>
                    <option value="MACD">MACD</option>
                    <option value="MDX">Mass Index</option>
                    <option value="Momentum">Momentum</option>
                    <option value="MFI">Money Flow Index</option>
                    <option value="NVI">Neg Volume Index</option>
                    <option value="OBV">On Balance Volume</option>
                    <option value="Performance">Performance</option>
                    <option value="PPO">% Price Oscillator</option>
                    <option value="PVO">% Volume Oscillator</option>
                    <option value="PVI">Pos Volume Index</option>
                    <option value="PVT">Price Volume Trend</option>
                    <option value="ROC">Rate of Change</option>
                    <option value="RSI" selected>RSI</option>
                    <option value="SStoch">Slow Stochastic</option>
                    <option value="StochRSI">StochRSI</option>
                    <option value="TRIX">TRIX</option>
                    <option value="UO">Ultimate Oscillator</option>
                    <option value="Vol">Volume</option>
                    <option value="WilliamR">William's %R</option>
                </select><br />
                <select id="Indicator2" name="Indicator2" class="input" style="width:140px;">
                    <option value="None">None</option>
                    <option value="AccDist">Accumulation/Distribution</option>
                    <option value="AroonOsc">Aroon Oscillator</option>
                    <option value="Aroon">Aroon Up/Down</option>
                    <option value="ADX">Avg Directional Index</option>
                    <option value="ATR">Avg True Range</option>
                    <option value="BBW">Bollinger Band Width</option>
                    <option value="CMF">Chaikin Money Flow</option>
                    <option value="COscillator">Chaikin Oscillator</option>
                    <option value="CVolatility">Chaikin Volatility</option>
                    <option value="CLV">Close Location Value</option>
                    <option value="CCI">Commodity Channel Index</option>
                    <option value="DPO">Detrended Price Osc</option>
                    <option value="DCW">Donchian Channel Width</option>
                    <option value="EMV">Ease of Movement</option>
                    <option value="FStoch">Fast Stochastic</option>
                    <option value="MACD" selected>MACD</option>
                    <option value="MDX">Mass Index</option>
                    <option value="Momentum">Momentum</option>
                    <option value="MFI">Money Flow Index</option>
                    <option value="NVI">Neg Volume Index</option>
                    <option value="OBV">On Balance Volume</option>
                    <option value="Performance">Performance</option>
                    <option value="PPO">% Price Oscillator</option>
                    <option value="PVO">% Volume Oscillator</option>
                    <option value="PVI">Pos Volume Index</option>
                    <option value="PVT">Price Volume Trend</option>
                    <option value="ROC">Rate of Change</option>
                    <option value="RSI">RSI</option>
                    <option value="SStoch">Slow Stochastic</option>
                    <option value="StochRSI">StochRSI</option>
                    <option value="TRIX">TRIX</option>
                    <option value="UO">Ultimate Oscillator</option>
                    <option value="Vol">Volume</option>
                    <option value="WilliamR">William's %R</option>
                </select><br />
                <select id="Indicator3" name="Indicator3" class="input" style="width:140px;">
                    <option value="None" selected>None</option>
                    <option value="AccDist">Accumulation/Distribution</option>
                    <option value="AroonOsc">Aroon Oscillator</option>
                    <option value="Aroon">Aroon Up/Down</option>
                    <option value="ADX">Avg Directional Index</option>
                    <option value="ATR">Avg True Range</option>
                    <option value="BBW">Bollinger Band Width</option>
                    <option value="CMF">Chaikin Money Flow</option>
                    <option value="COscillator">Chaikin Oscillator</option>
                    <option value="CVolatility">Chaikin Volatility</option>
                    <option value="CLV">Close Location Value</option>
                    <option value="CCI">Commodity Channel Index</option>
                    <option value="DPO">Detrended Price Osc</option>
                    <option value="DCW">Donchian Channel Width</option>
                    <option value="EMV">Ease of Movement</option>
                    <option value="FStoch">Fast Stochastic</option>
                    <option value="MACD">MACD</option>
                    <option value="MDX">Mass Index</option>
                    <option value="Momentum">Momentum</option>
                    <option value="MFI">Money Flow Index</option>
                    <option value="NVI">Neg Volume Index</option>
                    <option value="OBV">On Balance Volume</option>
                    <option value="Performance">Performance</option>
                    <option value="PPO">% Price Oscillator</option>
                    <option value="PVO">% Volume Oscillator</option>
                    <option value="PVI">Pos Volume Index</option>
                    <option value="PVT">Price Volume Trend</option>
                    <option value="ROC">Rate of Change</option>
                    <option value="RSI">RSI</option>
                    <option value="SStoch">Slow Stochastic</option>
                    <option value="StochRSI">StochRSI</option>
                    <option value="TRIX">TRIX</option>
                    <option value="UO">Ultimate Oscillator</option>
                    <option value="Vol">Volume</option>
                    <option value="WilliamR">William's %R</option>
                </select><br />
                <select id="Indicator4" name="Indicator4" class="input" style="width:140px;">
                    <option value="None" selected>None</option>
                    <option value="AccDist">Accumulation/Distribution</option>
                    <option value="AroonOsc">Aroon Oscillator</option>
                    <option value="Aroon">Aroon Up/Down</option>
                    <option value="ADX">Avg Directional Index</option>
                    <option value="ATR">Avg True Range</option>
                    <option value="BBW">Bollinger Band Width</option>
                    <option value="CMF">Chaikin Money Flow</option>
                    <option value="COscillator">Chaikin Oscillator</option>
                    <option value="CVolatility">Chaikin Volatility</option>
                    <option value="CLV">Close Location Value</option>
                    <option value="CCI">Commodity Channel Index</option>
                    <option value="DPO">Detrended Price Osc</option>
                    <option value="DCW">Donchian Channel Width</option>
                    <option value="EMV">Ease of Movement</option>
                    <option value="FStoch">Fast Stochastic</option>
                    <option value="MACD">MACD</option>
                    <option value="MDX">Mass Index</option>
                    <option value="Momentum">Momentum</option>
                    <option value="MFI">Money Flow Index</option>
                    <option value="NVI">Neg Volume Index</option>
                    <option value="OBV">On Balance Volume</option>
                    <option value="Performance">Performance</option>
                    <option value="PPO">% Price Oscillator</option>
                    <option value="PVO">% Volume Oscillator</option>
                    <option value="PVI">Pos Volume Index</option>
                    <option value="PVT">Price Volume Trend</option>
                    <option value="ROC">Rate of Change</option>
                    <option value="RSI">RSI</option>
                    <option value="SStoch">Slow Stochastic</option>
                    <option value="StochRSI">StochRSI</option>
                    <option value="TRIX">TRIX</option>
                    <option value="UO">Ultimate Oscillator</option>
                    <option value="Vol">Volume</option>
                    <option value="WilliamR">William's %R</option>
                </select>
            </div>
            <div class="inputtitle" style="text-align:center">
                <input id="Button1" name="Button1" type="submit" class="input" value="Update Chart">
            </div>
            </form>
        </td>
        <td>
            <div style="font-weight:bold;  font-family:arial; font-size:20pt; margin:5px 0px 0px 5px">
                ChartDirector Financial Chart Demonstration
            </div>
            <hr style="border:solid 1px #000080" />
            <br />
            <img id="ChartImage" align="top" border="0">
        </td>
    </tr>
</table>
</body>
</html>