Пример #1
0
Файл: T.py Проект: icharm/quantc
def draw(df, xf, yf, tf, title='New Stock Chart'):
    '''
    Draw hight stock chart by pandas.DataFrame
    Args:
        df: dataFrame
        xf: field name of dataFrame to x coordinate, the value of x must be unique datetime.
        yf: field name of dataFrame to y coordinate.
        tf: field name of dataFrame to line title.
    '''
    uniques = df[tf].unique()
    # log.debug(str(uniques))
    c = Highstock()
    for unique in uniques:
        sel_df = df[df[tf] == unique]
        sel_df = sel_df.loc[:, [xf, yf]]
        xyl = xy(sel_df)
        c.add_data_set(xyl, series_type='line', name=unique)

    options = {
        'title': {
            'text': title
        },
    }
    c.set_dict_options(options)
    c.save_file()
    log.info('Successful completion of drawing.')
    def plot(self, plt_type='all'):

        self.getsecID(sec=1)

        if not exist:
            return self.etf + ' ceased trading'

        #這邊廢code很多QQ
        #為避免error,先創一堆空的dict(相信這邊有更好的寫法XD)

        nav_data = dict()
        price_data = dict()
        volume_data = dict()
        nav_data[self.etf] = dict()
        price_data[self.etf] = dict()
        volume_data[self.etf] = dict()

        if plt_type == 'all':
            nav_data = self.data(data_type='nav')
            price_data = self.data(data_type='price')
            volume_data = self.data(data_type='volume')
        elif plt_type == 'nav':
            nav_data = self.data(data_type='nav')
        elif plt_type == 'price':
            price_data = self.data(data_type='price')
        else:
            return 'Sorry, plt_type should be nav, price or all'

        #用highchart裡的hichstock畫圖
        H = Highstock()

        nav_high_data = [[
            datetime.strptime(i, "%Y-%m-%d"),
            nav_data[list(nav_data.keys())[0]][i]
        ] for i in nav_data[list(nav_data.keys())[0]]]
        price_high_data = [[
            datetime.strptime(i, "%Y-%m-%d"),
            price_data[list(price_data.keys())[0]][i]
        ] for i in price_data[list(price_data.keys())[0]]]
        volume_high_data = [[
            datetime.strptime(i, "%Y-%m-%d"),
            volume_data[list(volume_data.keys())[0]][i]
        ] for i in volume_data[list(volume_data.keys())[0]]]

        if plt_type == 'all':
            H.add_data_set(price_high_data,
                           'line',
                           'price',
                           id='dataseries',
                           color='#969696',
                           tooltip={'valueDecimals': 4})
            H.add_data_set(nav_high_data,
                           'line',
                           'nav',
                           id='dataseries',
                           tooltip={'valueDecimals': 4})
            H.add_data_set(volume_high_data,
                           'column',
                           'Volume',
                           yAxis=1,
                           color='#969696')
        elif plt_type == 'nav':
            H.add_data_set(nav_high_data,
                           'line',
                           'nav',
                           id='dataseries',
                           tooltip={'valueDecimals': 4})
        elif plt_type == 'price':
            H.add_data_set(price_high_data,
                           'line',
                           'price',
                           id='dataseries',
                           color='#969696',
                           tooltip={'valueDecimals': 4})

        options = {
            'rangeSelector': {
                'selected': 4
            },
            'title': {
                'text': self.etf.upper()
            },
            'tooltip': {
                'style': {
                    'width': '200px'
                },
                'shared':
                True,
                'pointFormat':
                '<span style="color:{series.color}">{series.name}</span>: <b>{point.y}</b><br/>',
            },
            'yAxis': [{
                'labels': {
                    'align': 'right',
                    'x': -3
                },
                'title': {
                    'text': 'USD'
                },
                'height': '60%',
                'lineWidth': 0.5
            }, {
                'labels': {
                    'align': 'right',
                    'x': -3
                },
                'title': {
                    'text': 'Volume'
                },
                'top': '65%',
                'height': '35%',
                'offset': 0,
                'lineWidth': 2
            }],
            #'plotOptions': {'series': {'compare': 'percent'}},
        }

        H.set_dict_options(options)

        return H
H.add_data_set(data, 'line', 'USD to EUR', id='dataseries')
H.add_data_set(data2,
               'flags',
               onSeries='dataseries',
               shape='circlepin',
               width=16)

options = {
    'rangeSelector': {
        'selected': 0
    },
    'title': {
        'text': 'USD to EUR exchange rate'
    },
    'tooltip': {
        'style': {
            'width': '200px'
        },
        'valueDecimals': 4,
        'shared': True
    },
    'yAxis': {
        'title': {
            'text': 'Exchange rate'
        }
    },
}

H.set_dict_options(options)

H.htmlcontent
Пример #4
0
names = ["MSFT", "AAPL", "GOOG"]

for name in names:
    data_url = "http://www.highcharts.com/samples/data/jsonp.php?filename=" + name.lower() + "-c.json&callback=?"
    data = jsonp_loader(data_url, sub_d=r"(\/\*.*\*\/)")

    H.add_data_set(data, "line", name)


options = {
    "rangeSelector": {"selected": 4},
    "yAxis": {
        "labels": {
            "formatter": "function () {\
                            return (this.value > 0 ? ' + ' : '') + this.value + '%';\
                        }"
        },
        "plotLines": [{"value": 0, "width": 2, "color": "silver"}],
    },
    "plotOptions": {"series": {"compare": "percent"}},
    "tooltip": {
        "pointFormat": '<span style="color:{series.color}">{series.name}</span>: <b>{point.y}</b> ({point.change}%)<br/>',
        "valueDecimals": 2,
    },
}

H.set_dict_options(options)

H.htmlcontent
Пример #5
0
    def plot(self,
             title=False,
             width=600,
             height=300,
             load_js=None,
             js_sources=[]):
        chart_height = (height +
                        50) * self.number_of_candlestick_charts - 50 + 200
        H = Highstock(width=width, height=chart_height)
        groupingUnits = [
            ['hour', [60]],
        ]

        with open(
                os.path.join(os.path.dirname(os.path.abspath(__file__)),
                             "files/load.jinja")) as f:
            load_tempate = jinja2.Template(f.read())

        options = {
            'rangeSelector': {
                'buttons': [{
                    'type': 'minute',
                    'count': 180,
                    'text': '3h'
                }, {
                    'type': 'minute',
                    'count': 360,
                    'text': '6h'
                }, {
                    'type': 'minute',
                    'count': 720,
                    'text': '12h'
                }, {
                    'type': 'minute',
                    'count': 60 * 24,
                    'text': '1d'
                }, {
                    'type': 'minute',
                    'count': 60 * 48,
                    'text': '2d'
                }, {
                    'type': 'second',
                    'count': 1,
                    'text': '1s'
                }, {
                    'type': 'second',
                    'count': 10,
                    'text': '10s'
                }],
                'selected':
                4,
                'inputEnabled':
                False
            },
            'plotOptions': {
                'series': {
                    'turboThreshold:': 0,
                    'dataGrouping': {
                        'enabled': False,
                    },
                    'animation': False,
                },
                'scatter': {
                    'turboThreshold:': 0,
                    'marker': {
                        'radius': 5,
                        'symbol': 'circle',
                        'states': {
                            'hover': {
                                'enabled': True,
                                'lineColor': 'rgb(100,100,100)'
                            }
                        }
                    },
                    'tooltip': {
                        'pointFormat': '{series.name} x:{point.x}, y:{point.y}'
                        #'pointFormatter': """
                        #function () { debugger; }
                        #"""
                    },
                    'states': {
                        'hover': {
                            'marker': {
                                'enabled': False
                            }
                        }
                    },
                },
            },
            'navigator': {
                'enabled': True,
                'height': 20
            },
            'chart': {
                'animation': False,
            },
            'title': {
                'text': '',
                'style': {
                    'display': 'none'
                }
            },
            'legend': {
                'enabled': True,
            },
            'yAxis': []
        }
        if title:
            options['title'] = {'text': title}

        if load_js:
            load_js = load_tempate.render(symbols=self.symbols)
            options['event']['load'] = load_js

        idx = -1
        current_y = -height
        for chart_type, symbol, data, is_new_chart in self.symbols:
            if is_new_chart:
                idx += 1
                current_y += height + 50
                options['yAxis'].append({
                    'labels': {
                        'align': 'right',
                        'x': -3
                    },
                    'title': {
                        'text': 'y'
                    },
                    'top': '%dpx' % (current_y),
                    'height': '%dpx' % (height),
                    'lineWidth': 2,
                    'offset': 0,
                })

            if chart_type == 'candlestick':
                H.add_data_set(data,
                               chart_type,
                               symbol,
                               yAxis=idx,
                               dataGrouping={})
            elif chart_type == 'line':
                H.add_data_set(data,
                               chart_type,
                               symbol,
                               yAxis=idx,
                               dataGrouping={})
            elif chart_type == 'scatter':
                H.add_data_set(data,
                               chart_type,
                               symbol,
                               yAxis=idx,
                               dataGrouping={},
                               turboThreshold=0)

        H.set_dict_options(options)
        H.add_JSscript(self.theme, "head")
        for js_source in js_sources:
            H.add_JSsource(js_source)
        return H
Пример #6
0
    def plot(self,
             title=False,
             width=600,
             height=300,
             load_js=None,
             js_sources=[]):
        chart_height = (height + 50) * 1 - 50 + 200
        H = Highstock(width=width, height=chart_height)
        groupingUnits = [
            ['hour', [60]],
        ]

        with open(
                os.path.join(os.path.dirname(os.path.abspath(__file__)),
                             "files/load.jinja")) as f:
            load_tempate = jinja2.Template(f.read())

        options = {
            'rangeSelector': {
                'buttons': [{
                    'type': 'minute',
                    'count': 180,
                    'text': '3h'
                }, {
                    'type': 'minute',
                    'count': 360,
                    'text': '6h'
                }, {
                    'type': 'minute',
                    'count': 720,
                    'text': '12h'
                }, {
                    'type': 'minute',
                    'count': 60 * 24,
                    'text': '1d'
                }, {
                    'type': 'minute',
                    'count': 60 * 48,
                    'text': '2d'
                }, {
                    'type': 'second',
                    'count': 1,
                    'text': '1s'
                }, {
                    'type': 'second',
                    'count': 10,
                    'text': '10s'
                }],
                'selected':
                4,
                'inputEnabled':
                False
            },
            'plotOptions': {
                'series': {
                    'turboThreshold:': 0,
                    'dataGrouping': {
                        'enabled': False,
                    },
                    'animation': False
                }
            },
            'navigator': {
                'enabled': True,
                'height': 20
            },
            'chart': {
                'animation': False,
            },
            'legend': {
                'enabled': True,
            },
            'yAxis': [],
            'title': {
                'text': '',
                'style': {
                    'display': 'none'
                }
            },
        }
        if title:
            options['title'] = {'text': title}

        if load_js:
            load_js = load_tempate.render(symbols=self.symbols)
            options['event']['load'] = load_js

        symbol_data = {}
        for idx, (symbol, data) in enumerate(self.symbols):
            symbol_data[symbol] = data

            options['yAxis'].append({
                'labels': {
                    'align': 'right',
                    'x': -3
                },
                'title': {
                    'text': 'y'
                },
                'lineWidth': 2,
                'offset': 0,
            })

            H.add_data_set(data, 'line', symbol, dataGrouping={})

        H.set_dict_options(options)
        H.add_JSscript(self.theme, "head")
        for js_source in js_sources:
            H.add_JSsource(js_source)
        return H
Пример #7
0
data = Load.FinData(dataset='TaiwanStockPrice',
                    select='2330',
                    date='2018-10-10')

print(' change type of dataframe to highcharts ')
data['date'] = date2millisecond(data['date'])
list_data = []
for i in range(len(data)):
    tem = [
        int(data.loc[i, 'date']),
        float(data.loc[i, 'max']),
        float(data.loc[i, 'min'])
    ]
    list_data.append(tem)

chart.add_data_set(list_data, 'arearange', 'Temperatures')

options = {
    'rangeSelector': {
        'selected': 2
    },
    'title': {
        'text': 'Temperature variation by day'
    },
}

chart.set_dict_options(options)
# This will generate and save a .html file at the location you assign
chart.save_file()
Пример #8
0
def hc_candlestick_and_volume(data, title, theme=None):
    H = Highstock()

    ohlc = []
    volume = []
    groupingUnits = [['week', [1]], ['month', [1, 2, 3, 4, 6]]]

    for i in range(len(data)):
        ohlc.append([
            data[i][0],  # the date
            data[i][1],  # open
            data[i][2],  # high
            data[i][3],  # low
            data[i][4]  # close
        ])
        volume.append([
            data[i][0],  # the date
            data[i][5]  # the volume
        ])

    options = {
        'rangeSelector': {
            'selected': 1
        },
        'title': {
            'text': title
        },
        'yAxis': [{
            'labels': {
                'align': 'right',
                'x': -3
            },
            'title': {
                'text': 'OHLC'
            },
            'height': '60%',
            'lineWidth': 2
        }, {
            'labels': {
                'align': 'right',
                'x': -3
            },
            'title': {
                'text': 'Volume'
            },
            'top': '65%',
            'height': '35%',
            'offset': 0,
            'lineWidth': 2
        }],
    }

    H.add_data_set(ohlc,
                   'candlestick',
                   'OHLC',
                   dataGrouping={'units': groupingUnits})

    H.add_data_set(volume,
                   'column',
                   'Volume',
                   yAxis=1,
                   dataGrouping={'units': groupingUnits})

    H.set_dict_options(options)

    return hc_plot(H, title, theme)
Пример #9
0
def pplot_from_df(df, kind='line', stock=False, y_axes=None, debug=False, **kwargs):
    """

    :param df:    (pd.DataFrame): Column names become series names
    :param kind:   (str or list): Use list of same length as number of cols for multiple plot types
                                 (e.g. ['line_w_col', 'column'])
    :param stock:
    :param y_axes:
    :param debug:         (bool): Pretty prints options json
    :param kwargs:            (): Plotting options such as resolution, labels, color, etc.
    :return:
    """

    # If series given instead of DataFrame, convert:
    df = pd.DataFrame(df)

    # If no columns, do nothing:
    if df.shape[1] == 0:
        print("nothing to plot...")
        return

    # Create a color list for each line to plot:
    if 'colors' not in kwargs:
        kwargs['colors'] = map(
            lambda val: val.get_hex_l(),
            list(Color('#1492FB').range_to(Color('#27662A'), df.shape[1]))
        )

    # Set y axis
    if y_axes is None:
        y_axes = [1] * df.shape[1]
    else:
        assert (len(y_axes) == df.shape[1]), "y_axes must be list of same length as dataframe columns"
        # Should also assert something about the values in this list... later

    # Create highcharts_df object:
    # (Use height and width if provided):
    if stock:
        hc_visualization = Highstock(**{key: kwargs.pop(key) for key in ['width', 'height'] if key in kwargs})
    else:
        hc_visualization = Highchart(**{key: kwargs.pop(key) for key in ['width', 'height'] if key in kwargs})

    # Set timeseries encoding automatically:
    if df.index.is_all_dates:
        # NOTE *** AFTER monthes, I finally figured out that certain types of option settings
        # were the cause of time not encoding properly.  Basically, customer jinja-js like {x.point} functions
        # need to be created carefully with timestamps in mind.  Also, data must be encoded as 32 bit datetime.
        # (e.g. df.index.map(lambda dt: dt.to_datetime()), or [(datetime.datetime(2016,9,28), $val), ...]
        hc_visualization.set_options('xAxis', {'type': 'datetime'})

    # Set options (chart customization):
    options = get_highchart_options(**kwargs)

    if kind == 'line':
        for col_idx, col in enumerate(df.columns):
            hc_visualization = _highcharts_add_data_set(hc_visualization, df[col], col, kind, y_axes[col_idx])

    elif kind == 'bar' or kind == 'column':
        # Add bar categories (dataframe index):
        options['xAxis']['categories'] = list(df.index)

        # Add grouped column/bar data:
        for col_idx, col in enumerate(df.columns):
            hc_visualization = _highcharts_add_data_set(hc_visualization, df[col], col, kind, y_axes[col_idx])

    elif isinstance(kind, list):

        # Make sure the kind list is the right size:
        assert len(kind) == df.shape[1], "list kind must be the same length as df"

        # If columns in the list, map to line_w_col:
        if 'bar' in kind or 'column' in kind:
            kind = map(lambda a_kind: ("%s_w_col" % a_kind) if a_kind == 'line' else a_kind, kind)

        # Add categories:
        options['xAxis']['categories'] = list(df.index.values)

        # Add grouped data:
        for col_idx, col in enumerate(df.columns):
            hc_visualization = _highcharts_add_data_set(hc_visualization, df[col], col, kind[col_idx], y_axes[col_idx])

    # Set options:
    hc_visualization.set_dict_options(options)

    # Print options json if debug mode is on:
    if debug:
        pp = pprint.PrettyPrinter(indent=1)
        pp.pprint(options)

    return hc_visualization
Пример #10
0
def hc_candlestick_and_volume(data, title, theme=None):
    H = Highstock()

    ohlc = []
    volume = []
    groupingUnits = [
        ['week', [1]],
        ['month', [1, 2, 3, 4, 6]]
    ]

    for i in range(len(data)):
        ohlc.append(
            [
            data[i][0], # the date
            data[i][1], # open
            data[i][2], # high
            data[i][3], # low
            data[i][4]  # close
            ]
            )
        volume.append(
            [
            data[i][0], # the date
            data[i][5]  # the volume
            ]
        )

    options = {
        'rangeSelector': {
            'selected': 1
        },

        'title': {
            'text': title
        },

        'yAxis': [{
            'labels': {
                'align': 'right',
                'x': -3
            },
            'title': {
                'text': 'OHLC'
            },
            'height': '60%',
            'lineWidth': 2
        }, {
            'labels': {
                'align': 'right',
                'x': -3
            },
            'title': {
                'text': 'Volume'
            },
            'top': '65%',
            'height': '35%',
            'offset': 0,
            'lineWidth': 2
        }],
    }

    H.add_data_set(ohlc, 'candlestick', 'OHLC', dataGrouping = {
        'units': groupingUnits
    })

    H.add_data_set(volume, 'column', 'Volume', yAxis = 1, dataGrouping = {
        'units': groupingUnits
    })

    H.set_dict_options(options)

    return hc_plot(H, title, theme)
Пример #11
0
def creatChart(klines, symbol):
    filename = "%s_dayk" % symbol
    ohlc = []
    volume = []
    for line in klines:
        ohlc.append([
            line[0],
            #open
            float(line[1]),
            #high
            float(line[2]),
            # low
            float(line[3]),
            # close
            float(line[4])
        ])
        volume.append([line[0], float(line[5])])

    H = Highstock()

    groupingUnits = [['day', [1]]]

    options = {
        'rangeSelector': {
            'selected': 4
        },
        'chart': {
            'zoomType': 'x',
            'reflow': True,
            'height': 900
        },
        'title': {
            'text': '%s' % (symbol)
        },
        'subtitle': {
            'text': filename
        },
        'yAxis': [{
            'labels': {
                'align': 'right',
                'x': -3
            },
            'title': {
                'text': 'OHLC'
            },
            'height': '60%',
            'lineWidth': 2
        }, {
            'labels': {
                'align': 'right',
                'x': -3
            },
            'title': {
                'text': 'Volume'
            },
            'top': '65%',
            'height': '35%',
            'offset': 0,
            'lineWidth': 2
        }],
    }

    H.add_data_set(ohlc,
                   'candlestick',
                   symbol,
                   dataGrouping={'units': groupingUnits})
    H.add_data_set(volume,
                   'column',
                   'Volume',
                   yAxis=1,
                   dataGrouping={'units': groupingUnits})

    H.set_dict_options(options)

    H.save_file(filename)

    print("===--- Completed ---===")
Пример #12
0
def main(argv):
    from highcharts import Highstock  # 暂时这样避免其他脚本 import 出错
    unittest()

    opts, args = parse_options(argv)
    #exchanges = ('binance', 'huobipro', 'okex', 'zb')
    #symbol = 'qtum/btc'
    #sdt = '2018-01-20 18-00-00'
    #edt = '2018-01-20 18-59-00'
    #folder = r'C:\Users\Eph\Desktop\CC\trade\ccxtbot\CCMarkets\data'
    exchanges = args
    symbol = opts.get('symbol')
    sdt = opts.get('start')
    edt = opts.get('stop')
    folder = opts.get('folder')
    prec = int(opts.get('prec', 8))
    fname = opts.get('file', 'StockChart')
    verbose = opts.get('verbose', False)
    if not exchanges or not symbol or not sdt or not edt or not folder:
        usage(argv[0])
        return 2

    options = {
        'chart': {
            'height': '50%', # default None
        },
        'title': {
            'text': 'Price Difference',
        },
        "tooltip": {
            "xDateFormat": "%Y-%m-%d %H:%M:%S %A",  # NOTE: BUG, 设置不生效
            'pointFormat': '<span style="color:{point.color}">' + '\\u25CF'.decode('unicode-escape') + \
                           '</span> {series.name}: <b>{point.y}</b><br/>',
            'padding': 1,   # default 8
        },
        "legend": {
            "enabled": True
        },
        "xAxis": {
            "type": "datetime"
        },
        'yAxis': {
            'title': {
                'text': '价格',
            },
            'opposite': True,   # False 表示在左边显示,默认值为 True
        },
         # 这是K线图的属性,不画K线图的话不需要
        "plotOptions": {
            "candlestick": {
                "color": "#d75442",
                "upColor": "#6ba583"
            }
        },
        "rangeSelector": {
            "buttons": [
                {
                    "type"  : "hour",
                    "count" : 1,
                    "text"  : "1h",
                },
                {
                    "type"  : 'hour',
                    "count" : 3,
                    "text"  : "3h"
                },
                {
                    "type"  : "hour",
                    "count" : 6,
                    "text"  : "6h"
                },
                {
                    "type"  : "hour",
                    "count" : 12,
                    "text"  : "12h"
                },
                {
                    "type"  : "all",
                    "text"  : "All"
                }
            ],
            #"selected": 2,  # 默认选择的索引号,从0开始,默认值为 undefined
            "inputEnabled": True,
            "inputBoxWidth": 150, # default 90
            'inputDateFormat': '%Y/%m/%d %H:%M:%S',
            'inputEditDateFormat': '%Y/%m/%d %H:%M:%S',
        },
    }

    chart = Highstock()
    chart.set_options('global', {'timezoneOffset': -8 * 60})

    chart.set_dict_options(options)
    #chart.set_options('chart', {'height': None})
    chart.set_options(
        'tooltip',
        {
            #'pointFormat': '<span style="color:{point.color}">' + '\\u25CF'.decode('unicode-escape') + \
            #'</span> {series.name}: <b>{point.y:.%df}</b><br/>' % prec,
            'valueDecimals': prec,
            'valueSuffix':
            ' ' + symbol.replace('/', '_').upper().split('_')[1],
        })

    ers = []
    stats = {}
    for exchange in exchanges:
        inst = ExchangeRecords(exchange, symbol, sdt, edt, folder)
        ers.append(inst)
        stats[exchange] = {}
        stats[exchange]['records_count'] = 0

    chart.set_options('subtitle', {'text': ers[0].symbol.replace('_', '/')})

    # 'binance': {'Buy': x, 'Sell': y}
    all_records = {}
    loop_count = 0
    # 标记哪个交易所已经拿完数据了
    # {exchange: True/False, ...}
    ctl = {}
    for inst in ers:
        ctl[inst.exchange] = False

    while True:
        all_true = True
        for v in ctl.itervalues():
            if not v:
                all_true = False
                break
        if ctl and all_true:
            break

        loop_count += 1
        for inst in ers:
            record = inst.get_one_record()
            if record is None:
                ctl[inst.exchange] = True
                continue

            records_dict = all_records.setdefault(inst.exchange, {})
            if not record:
                #ctl[inst.exchange] = True
                continue
            stats[inst.exchange]['records_count'] += 1

            # 不需要小数点
            t = int(int(float(record[0])) * 1000)
            #t += 3600*1000*8 # GMT+8
            buy = records_dict.setdefault('buy', [])
            buy.append([t, float(record[1])])
            sell = records_dict.setdefault('sell', [])
            sell.append([t, float(record[2])])
            last = records_dict.setdefault('last', [])
            last.append([t, float(record[3])])

    if verbose:
        print('loop_count:', loop_count)
        Log(json.dumps(stats, indent=4, sort_keys=True))
    for exchange in exchanges:
        records_dict = all_records[exchange]
        chart.add_data_set(records_dict['buy'],
                           series_type='line',
                           name='%s buy' % exchange)
        chart.add_data_set(records_dict['sell'],
                           series_type='line',
                           name='%s sell' % exchange)
        chart.add_data_set(records_dict['last'],
                           series_type='line',
                           name='%s last' % exchange)
    # 后缀名值 .html
    chart.save_file(filename=fname)
    Log('Successfully write to the file:', fname + '.html')