如何在领域十字线中显示音量并以编程方式设置十字线?

huangapple go评论65阅读模式
英文:

How to display volume in the domain crosshair and set the crosshair programmatically?

问题

这个问题与我之前的问题相关链接。感谢@trashgod,我已经解决了许多与那个问题有关的问题,我将在这个问题中问剩下的部分。我的示例代码如下:

import java.awt.*;
import java.awt.event.*;
import java.text.*;
import java.util.*;
import javax.swing.*;
import org.jfree.chart.*;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.entity.*;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYBarRenderer;
import org.jfree.data.time.*;
import org.jfree.data.xy.XYDataset;
import org.jfree.chart.labels.*;
import org.jfree.chart.panel.*;
import org.jfree.chart.plot.*;

public class PriceVolume_Chart extends JPanel implements ChartMouseListener {
    // ...(代码的其余部分)
}

// ...(代码的其余部分)

class MyRender extends XYBarRenderer {
    // ...(代码的其余部分)
}

// ...(代码的其余部分)

我的问题是:

[1] 如何在十字线上正确显示交易量数据 [ 目前显示的值不正确,但我不知道如何修复 ]?
[2] 如何在应用程序启动时将鼠标指向第 N 天 [ 例如第 12 天 ]?

对于第二个问题的更多细节,当我移动鼠标时,我可以在屏幕上输出鼠标的 x、y 值,我可以执行以下操作来在启动时设置鼠标在所需位置:

// ...
panel.addOverlay(crosshairOverlay);
add(panel);
xCrosshair.setValue(1.5700752E12);
yCrosshair.setValue(119.959007);

但是因为我不知道第 N 天的 [x,y] 位置,所以我不知道如何在那一天设置鼠标。

我想要做的原因是:在我的应用程序中,我想要查看另一个面板上某一天的一些数据,用户可以点击某一天,我希望 JFreeChart 可以立即在图表上指向那一天。

因此,我的第二个问题是在询问:“如何获取图表上第 N 天的 [x,y] 位置?”

英文:

This question is related to my previous question [ https://stackoverflow.com/questions/64106159/how-to-show-cross-lines-and-red-color-for-price-drop-in-jfreechart ]. Thanks to @trashgod I have solved a lot of issues from that question, I'll ask what's left in this question. My sample code looks like this :

import java.awt.*;
import java.awt.event.*;
import java.text.*;
import java.util.*;
import javax.swing.*;
import org.jfree.chart.*;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.entity.*;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYBarRenderer;
import org.jfree.data.time.*;
import org.jfree.data.xy.XYDataset;
import org.jfree.chart.labels.*;
import org.jfree.chart.panel.*;
import org.jfree.chart.plot.*;
public class PriceVolume_Chart extends JPanel implements ChartMouseListener    // A demo application for price-volume chart.   
{
ChartPanel panel;
TimeSeries Price_series=new TimeSeries("Price");
TimeSeries Volume_Series=new TimeSeries("Volume");
Crosshair xCrosshair,yCrosshair;
static Vector<String> Volume_Color_Vector=new Vector();
public PriceVolume_Chart(String Symbol)
{
JFreeChart chart=createChart(Symbol);
panel=new ChartPanel(chart,true,true,true,false,true);
panel.setPreferredSize(new Dimension(1000,500));
panel.addChartMouseListener(this);
CrosshairOverlay crosshairOverlay=new CrosshairOverlay();
float[] dash={2f,0f,2f};
BasicStroke bs=new BasicStroke(1,BasicStroke.CAP_BUTT,BasicStroke.JOIN_ROUND,1.0f,dash,2f);
xCrosshair=new Crosshair(Double.NaN,Color.GRAY,bs);
xCrosshair.setLabelBackgroundPaint(new Color(0f,0f,0f,1f));
xCrosshair.setLabelFont(xCrosshair.getLabelFont().deriveFont(14f));
xCrosshair.setLabelPaint(new Color(1f,1f,1f,1f));
xCrosshair.setLabelVisible(true);
yCrosshair=new Crosshair(Double.NaN,Color.GRAY,bs);
yCrosshair.setLabelBackgroundPaint(new Color(0f,0f,0f,1f));
yCrosshair.setLabelFont(xCrosshair.getLabelFont().deriveFont(14f));
yCrosshair.setLabelPaint(new Color(1f,1f,1f,1f));
yCrosshair.setLabelVisible(true);
crosshairOverlay.addDomainCrosshair(xCrosshair);
crosshairOverlay.addRangeCrosshair(yCrosshair);
panel.addOverlay(crosshairOverlay);
add(panel);
xCrosshair.setValue(1.5700752E12);
xCrosshair.setVisible(true);
yCrosshair.setValue(119.959007);
yCrosshair.setVisible(true);
}
private JFreeChart createChart(String Symbol)
{
createPriceDataset(Symbol);
XYDataset priceData=new TimeSeriesCollection(Price_series);
JFreeChart chart=ChartFactory.createTimeSeriesChart(Symbol,"Date",getYLabel("Price ( $ )"),priceData,true,true,true);
XYPlot plot=chart.getXYPlot();
plot.setBackgroundPaint(new Color(192,196,196));
NumberAxis rangeAxis1=(NumberAxis)plot.getRangeAxis();
rangeAxis1.setLowerMargin(0.40);                                           // Leave room for volume bars
//    plot.getRenderer().setDefaultToolTipGenerator(new StandardXYToolTipGenerator(StandardXYToolTipGenerator.DEFAULT_TOOL_TIP_FORMAT,DateFormat.getDateInstance(), NumberFormat.getCurrencyInstance()));
plot.getRenderer().setDefaultToolTipGenerator(new StandardXYToolTipGenerator(StandardXYToolTipGenerator.DEFAULT_TOOL_TIP_FORMAT,new SimpleDateFormat("yyyy-MM-d"),NumberFormat.getCurrencyInstance()));
//    DecimalFormat format=new DecimalFormat("00.00");
//    rangeAxis1.setNumberFormatOverride(format);
//    rangeAxis1.setNumberFormatOverride(NumberFormat.getCurrencyInstance());  
NumberAxis rangeAxis2=new NumberAxis("Volume");
rangeAxis2.setUpperMargin(1.00);                                           // Leave room for price line   
rangeAxis2.setNumberFormatOverride(NumberFormat.getNumberInstance());
plot.setRangeAxis(1,rangeAxis2);
plot.setDataset(1,new TimeSeriesCollection(Volume_Series));
plot.setRangeAxis(1,rangeAxis2);
plot.mapDatasetToRangeAxis(1,1);
plot.setRenderer(1,new MyRender());
return chart;
}
private void createPriceDataset(String Symbol)
{
String Lines[]=new String[21], Items[], Date;
int Year, Month, Day;
long Volume,Last_Volume=0;
double Price;
Lines[0]="Date,Open,High,Low,Close,Adj Close,Volume";
Lines[1]="2019-09-23,129.589996,130.710007,128.240005,129.300003,126.555969,553700";
Lines[2]="2019-09-24,129.309998,129.529999,125.500000,126.750000,124.060089,732900";
Lines[3]="2019-09-25,126.570000,128.500000,126.190002,127.879997,125.166100,422000";
Lines[4]="2019-09-26,127.849998,128.589996,127.169998,127.779999,125.068230,376100";
Lines[5]="2019-09-27,128.669998,129.289993,126.389999,126.419998,123.737083,332900";
Lines[6]="2019-09-30,126.589996,128.789993,125.849998,128.130005,125.410797,456700";
Lines[7]="2019-10-01,129.039993,130.899994,125.480003,126.040001,123.365158,322700";
Lines[8]="2019-10-02,125.059998,125.180000,121.620003,123.120003,120.507126,577100";
Lines[9]="2019-10-03,122.650002,123.320000,119.089996,122.559998,119.959007,581300";
Lines[10]="2019-10-04,122.970001,123.949997,121.320000,123.879997,121.250992,315700";
Lines[11]="2019-10-07,123.139999,124.610001,122.669998,122.879997,120.272217,510300";
Lines[12]="2019-10-08,121.720001,121.879997,118.089996,118.660004,116.141777,616600";
Lines[13]="2019-10-09,119.410004,119.610001,116.680000,118.419998,115.906868,603300";
Lines[14]="2019-10-10,119.089996,121.209999,117.080002,118.209999,115.701324,483300";
Lines[15]="2019-10-11,120.330002,123.040001,119.720001,122.550003,119.949226,700500";
Lines[16]="2019-10-14,122.550003,123.720001,120.940002,122.540001,119.939430,492900";
Lines[17]="2019-10-15,122.849998,124.220001,121.230003,123.699997,121.074814,598200";
Lines[18]="2019-10-16,123.889999,124.849998,122.800003,123.209999,120.595207,663600";
Lines[19]="2019-10-17,123.449997,124.889999,122.790001,123.360001,120.742035,563200";
Lines[20]="2019-10-18,123.050003,124.620003,122.459999,123.540001,120.918213,650300";
for (int i=1;i<Lines.length;i++)
{
Items=Lines[i].split(",");
Date=Items[0].replace("-0","-");
Price=Double.parseDouble(Items[5]);
Volume=Long.parseLong(Items[6]);
Items=Date.split("-");
Year=Integer.parseInt(Items[0]);
Month=Integer.parseInt(Items[1]);
Day=Integer.parseInt(Items[2]);
Price_series.add(new Day(Day,Month,Year),Price);
Volume_Series.add(new Day(Day,Month,Year),Volume);
Volume_Color_Vector.add(Volume>=Last_Volume?"+":"-");
Last_Volume=Volume;
}
}
@Override
public void chartMouseClicked(ChartMouseEvent event)
{
// ignore
}
public void chartMouseMoved(ChartMouseEvent cmevent)
{
ChartEntity chartentity=cmevent.getEntity();
if (chartentity instanceof XYItemEntity)
{
XYItemEntity e=(XYItemEntity)chartentity;
XYDataset d=e.getDataset();
int s=e.getSeriesIndex();
int i=e.getItem();
double x=d.getXValue(s,i);
double y=d.getYValue(s,i);
//      Out("x = "+x+"  y = "+y);
xCrosshair.setValue(x);
yCrosshair.setValue(y);
}
}
String getYLabel(String Text)
{
String Result="";
for (int i=0;i<Text.length();i++) Result+=Text.charAt(i)+(i<Text.length()-1?"\u2009":"");
//    Out(Result);
return Result;
}
private static void out(String message) { System.out.print(message); }
private static void Out(String message) { System.out.println(message); }
// Create the GUI and show it. For thread safety, this method should be invoked from the event-dispatching thread.
static void Create_And_Show_GUI()
{
final PriceVolume_Chart demo=new PriceVolume_Chart("ADS");
JFrame frame=new JFrame("PriceVolume_Chart Frame");
frame.add(demo);
frame.addWindowListener(new WindowAdapter()
{
public void windowActivated(WindowEvent e) { }
public void windowClosed(WindowEvent e) { }
public void windowClosing(WindowEvent e) { System.exit(0); }
public void windowDeactivated(WindowEvent e) { }
public void windowDeiconified(WindowEvent e) { demo.repaint(); }
public void windowGainedFocus(WindowEvent e) { demo.repaint(); }
public void windowIconified(WindowEvent e) { }
public void windowLostFocus(WindowEvent e) { }
public void windowOpening(WindowEvent e) { demo.repaint(); }
public void windowOpened(WindowEvent e) { }
public void windowResized(WindowEvent e) { demo.repaint(); }
public void windowStateChanged(WindowEvent e) { demo.repaint(); }
});
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args)
{
// Schedule a job for the event-dispatching thread : creating and showing this application's GUI.
SwingUtilities.invokeLater(new Runnable() { public void run() { Create_And_Show_GUI(); } });
}
}
class MyRender extends XYBarRenderer
{
@Override
public Paint getItemPaint(int row,int col)
{
System.out.println(row+" "+col+" "+super.getItemPaint(row,col));
return PriceVolume_Chart.Volume_Color_Vector.elementAt(col).equals("+")?super.getItemPaint(row,col):new Color(0.56f,0.2f,0.5f,1f);
}
}

My questions are :

[1] How to correctly show Volume data in cross-hair [ it's now showing the incorrect value, yet I don't know how to fix it ] ?
[2] How to point the mouse on the N-th [ e.g. 12th ] day when the app starts ? 

More details to the 2nd question, when I move the mouse, I can output the x,y value of the mouse on the screen, and I can do the following to set the mouse at the desired location at start up :

...
panel.addOverlay(crosshairOverlay);
add(panel);
xCrosshair.setValue(1.5700752E12);
yCrosshair.setValue(119.959007);

But since I don't know the [x,y] location of the N-th day, therefore I don't know how to set the mouse on that day.

The reason I want to do this is : in my app, I want to look at some data on a certain day in another panel, and user can click on a certain day and I want the JFreeChart to instantly point to that day on the chart.

So my 2nd question is asking : "How can I get the [x,y] location of the N-th day on the chart ?

答案1

得分: 3

如何正确显示十字准线中的成交量数据;当前显示的值不正确。

此处建议," x 值为日期...在缺少格式的情况下,十字准线正确显示了从时代起的毫秒数。" 要更改十字准线的显示,添加一个自定义 CrosshairLabelGenerator。下面的实现会在您的成交量系列中搜索匹配的时间戳,并将相应的值格式化为数字。

xCrosshair.setLabelGenerator(new CrosshairLabelGenerator() {
   @Override
   public String generateLabel(Crosshair crosshair) {
       long ms = (long) crosshair.getValue();
       TimeSeriesDataItem item = null;
       for (int i = 0; i < Volume_Series.getItemCount(); i++) {
           item = Volume_Series.getDataItem(i);
           if (ms == item.getPeriod().getFirstMillisecond()) {
               break;
           }
       }
       long volume = item.getValue().longValue();
       return NumberFormat.getInstance().format(volume);
   }
});

考虑调整域轴的下边距以实现对称。

DateAxis domainAxis = (DateAxis) plot.getDomainAxis();
domainAxis.setLowerMargin(0.05);

还可以考虑将 MyRenderer 的对齐因子设置为 0.5,以使柱形位于价格点下方居中。

this.setBarAlignmentFactor(0.5);

如何在领域十字线中显示音量并以编程方式设置十字线?

由于用户控制鼠标,我建议避免以编程方式移动十字准线。相反,考虑使用适当的 AnnotationMarker。话虽如此,请比较您的 chartMouseMoved() 实现与此处所示的实现。您的实现针对每个新的 XYItemEntity 进行更新,因此十字准线会“捕捉”到点或柱形。相反地,后者会持续更新,通过 java2DToValue() 进行缩放,如此处所述。

英文:

>How to correctly show volume data in cross-hair; it's now showing the incorrect value.

As suggested here, "The x value is the date…absent formatting, the crosshair correctly displays the number of milliseconds from the epoch." To alter the crosshair's display, add a custom CrosshairLabelGenerator. The implementation below searches your volume series for the matching timestamp and formats the corresponding value as a number.

xCrosshair.setLabelGenerator(new CrosshairLabelGenerator() {
@Override
public String generateLabel(Crosshair crosshair) {
long ms = (long) crosshair.getValue();
TimeSeriesDataItem item = null;
for (int i = 0; i &lt; Volume_Series.getItemCount(); i++) {
item = Volume_Series.getDataItem(i);
if (ms == item.getPeriod().getFirstMillisecond()) {
break;
}
}
long volume = item.getValue().longValue();
return NumberFormat.getInstance().format(volume);
}
});

Consider adjusting the lower margin of the domain axis for symmetry.

DateAxis domainAxis = (DateAxis) plot.getDomainAxis();
domainAxis.setLowerMargin(0.05);

Also consider setting the alignment factor of MyRenderer to center the bar under the price point.

this.setBarAlignmentFactor(0.5);

如何在领域十字线中显示音量并以编程方式设置十字线?

As the user controls the mouse, I would avoid moving the crosshair programmatically. Instead, look at a suitable Annotation or Marker. Having said that, compare your implementation of chartMouseMoved() to the one shown here. Your's updates for each new XYItemEntity encountered, so the crosshairs "snap" to points or bars. In contrast, the latter one updates continuously, scaling via java2DToValue(), as described here.

huangapple
  • 本文由 发表于 2020年10月1日 10:30:47
  • 转载请务必保留本文链接:https://go.coder-hub.com/64148281.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定