Full Code of SlamDunk007/StockChart for AI

master ce2af05af1aa cached
72 files
1.8 MB
845.2k tokens
194 symbols
1 requests
Download .txt
Showing preview only (1,896K chars total). Download the full file or copy to clipboard to get everything.
Repository: SlamDunk007/StockChart
Branch: master
Commit: ce2af05af1aa
Files: 72
Total size: 1.8 MB

Directory structure:
gitextract_bedscd2c/

├── .gitignore
├── README.md
├── app/
│   ├── .gitignore
│   ├── build.gradle
│   ├── proguard-rules.pro
│   └── src/
│       ├── androidTest/
│       │   └── java/
│       │       └── com/
│       │           └── guannan/
│       │               └── stockchart/
│       │                   └── ExampleInstrumentedTest.java
│       ├── main/
│       │   ├── AndroidManifest.xml
│       │   ├── java/
│       │   │   └── com/
│       │   │       └── guannan/
│       │   │           └── stockchart/
│       │   │               └── MainActivity.java
│       │   └── res/
│       │       ├── drawable/
│       │       │   ├── ic_launcher_background.xml
│       │       │   └── selector_line_bg.xml
│       │       ├── drawable-v24/
│       │       │   └── ic_launcher_foreground.xml
│       │       ├── layout/
│       │       │   ├── activity_main.xml
│       │       │   └── pankou_layout.xml
│       │       ├── mipmap-anydpi-v26/
│       │       │   ├── ic_launcher.xml
│       │       │   └── ic_launcher_round.xml
│       │       └── values/
│       │           ├── colors.xml
│       │           ├── strings.xml
│       │           └── styles.xml
│       └── test/
│           └── java/
│               └── com/
│                   └── guannan/
│                       └── stockchart/
│                           └── ExampleUnitTest.java
├── build.gradle
├── chartmodule/
│   ├── .gitignore
│   ├── build.gradle
│   ├── consumer-rules.pro
│   ├── proguard-rules.pro
│   └── src/
│       └── main/
│           ├── AndroidManifest.xml
│           ├── java/
│           │   └── com/
│           │       └── guannan/
│           │           └── chartmodule/
│           │               ├── chart/
│           │               │   ├── BaseChartView.java
│           │               │   ├── KMasterChartView.java
│           │               │   ├── KSubChartView.java
│           │               │   ├── MarketFigureChart.java
│           │               │   └── ViewPortHandler.java
│           │               ├── data/
│           │               │   ├── ExtremeValue.java
│           │               │   ├── KLineToDrawItem.java
│           │               │   ├── LineRectItem.java
│           │               │   ├── SubChartData.java
│           │               │   └── TechItem.java
│           │               ├── helper/
│           │               │   ├── ChartDataSourceHelper.java
│           │               │   ├── ChartTouchHelper.java
│           │               │   ├── TechParamType.java
│           │               │   └── TechParamsHelper.java
│           │               ├── inter/
│           │               │   ├── IChartDataCountListener.java
│           │               │   ├── IChartGestureListener.java
│           │               │   ├── IPressChangeListener.java
│           │               │   └── ITouchResponseListener.java
│           │               └── utils/
│           │                   ├── DataUtils.java
│           │                   ├── DateUtils.java
│           │                   ├── DisplayUtils.java
│           │                   ├── LogUtils.java
│           │                   ├── NumFormatUtils.java
│           │                   ├── PaintUtils.java
│           │                   └── StringUtils.java
│           └── res/
│               └── values/
│                   ├── colors.xml
│                   └── strings.xml
├── gradle/
│   └── wrapper/
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
└── simulateddata/
    ├── .gitignore
    ├── build.gradle
    ├── consumer-rules.pro
    ├── libs/
    │   └── json-smart-2.1.1.jar
    ├── proguard-rules.pro
    └── src/
        └── main/
            ├── AndroidManifest.xml
            ├── assets/
            │   ├── geli.json
            │   ├── maotai.json
            │   ├── pingan.json
            │   └── slw_k.json
            ├── java/
            │   └── com/
            │       └── guannan/
            │           └── simulateddata/
            │               ├── LocalUtils.java
            │               ├── entity/
            │               │   └── KLineItem.java
            │               └── parser/
            │                   └── KLineParser.java
            └── res/
                └── values/
                    └── strings.xml

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
*.iml
.gradle
.idea
*.iml
*/build/
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/build
/captures
.externalNativeBuild
.cxx


================================================
FILE: README.md
================================================
# StockChart
自定义股票行情图,高仿某币app行情图**(持续更新中!!!)**具体绘制细节参考博客:
https://blog.csdn.net/kemeng7758/article/details/106729748
# 功能
1.支持行情图左右滑动   
2.支持行情图的惯性滑动   
3.支持行情图的方法和缩小   
4.支持BOLL和MACD技术指标(后面会继续丰富指标)   
5.支持主图副图动态添加,尺寸修改等   
6.支持长按滑动和长按弹框等
# 效果图

<img src="https://github.com/SlamDunk007/StockChart/blob/master/chart_dynamic.gif" width="280"/>  <img src="https://github.com/SlamDunk007/StockChart/blob/master/chart1.png" width="280" /> <img src="https://github.com/SlamDunk007/StockChart/blob/master/chart2.png" width="280"/>  <img src="https://github.com/SlamDunk007/StockChart/blob/master/scale_max.png" width="280"/>  <img src="https://github.com/SlamDunk007/StockChart/blob/master/scale_min.png" width="280"/>

# 项目关键类

```java
行情图容器:MarketFigureChart
行情图主图:KMasterChartView
行情图副图:KSubChartView(成交量、MACD)
手势处理:ChartTouchHelper
数据处理:ChartDataSourceHelper
```

# 使用方式(可参考MainActivity当中代码)

(1)布局当中引用

```java
<com.guannan.chartmodule.chart.MarketFigureChart
  android:id="@+id/chart_container"
  android:layout_width="match_parent"
  android:layout_height="wrap_content" />
```

(2)然后在代码当中动态添加即可

```java
// 行情图容器
mMarketFigureChart = findViewById(R.id.chart_container);

// 行情图主图(蜡烛线)
mKLineChartView = new KMasterChartView(this);
mMarketFigureChart.addChildChart(mKLineChartView, 200);

// 行情图附图(成交量)
mVolumeView = new KSubChartView(this);
mMarketFigureChart.addChildChart(mVolumeView, 100);

// MACD
mMacdView = new KSubChartView(this);
mMarketFigureChart.addChildChart(mMacdView, 100);
```


================================================
FILE: app/.gitignore
================================================
/build


================================================
FILE: app/build.gradle
================================================
apply plugin: 'com.android.application'

android {
    compileSdkVersion 29
    buildToolsVersion "29.0.2"
    defaultConfig {
        applicationId "com.guannan.stockchart"
        minSdkVersion 19
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'androidx.appcompat:appcompat:1.0.2'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    implementation project(':chartmodule')
}


================================================
FILE: app/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
#   http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
#   public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile


================================================
FILE: app/src/androidTest/java/com/guannan/stockchart/ExampleInstrumentedTest.java
================================================
package com.guannan.stockchart;

import android.content.Context;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;

import org.junit.Test;
import org.junit.runner.RunWith;

import static org.junit.Assert.*;

/**
 * Instrumented test, which will execute on an Android device.
 *
 * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
 */
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
  @Test
  public void useAppContext() {
    // Context of the app under test.
    Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();

    assertEquals("com.guannan.stockchart", appContext.getPackageName());
  }
}


================================================
FILE: app/src/main/AndroidManifest.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  package="com.guannan.stockchart">

  <application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">
    <activity android:name=".MainActivity">
      <intent-filter>
        <action android:name="android.intent.action.MAIN" />

        <category android:name="android.intent.category.LAUNCHER" />
      </intent-filter>
    </activity>
  </application>

</manifest>

================================================
FILE: app/src/main/java/com/guannan/stockchart/MainActivity.java
================================================
package com.guannan.stockchart;

import android.os.Bundle;
import android.os.Handler;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.RadioGroup;
import androidx.appcompat.app.AppCompatActivity;
import com.guannan.chartmodule.chart.KMasterChartView;
import com.guannan.chartmodule.chart.KSubChartView;
import com.guannan.chartmodule.chart.MarketFigureChart;
import com.guannan.chartmodule.data.ExtremeValue;
import com.guannan.chartmodule.data.KLineToDrawItem;
import com.guannan.chartmodule.data.SubChartData;
import com.guannan.chartmodule.helper.ChartDataSourceHelper;
import com.guannan.chartmodule.helper.TechParamType;
import com.guannan.chartmodule.inter.IChartDataCountListener;
import com.guannan.chartmodule.inter.IPressChangeListener;
import com.guannan.simulateddata.LocalUtils;
import com.guannan.simulateddata.parser.KLineParser;
import java.util.List;

public class MainActivity extends AppCompatActivity
    implements IChartDataCountListener<List<KLineToDrawItem>>, IPressChangeListener,
    RadioGroup.OnCheckedChangeListener {

  private ChartDataSourceHelper mHelper;
  private KMasterChartView mKLineChartView;
  private KSubChartView mVolumeView;
  private MarketFigureChart mMarketFigureChart;
  private ProgressBar mProgressBar;

  private int MAX_COLUMNS = 160;
  private int MIN_COLUMNS = 20;
  private KSubChartView mMacdView;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    initViews();

    // 行情图容器
    mMarketFigureChart = findViewById(R.id.chart_container);

    // 行情图主图(蜡烛线)
    mKLineChartView = new KMasterChartView(this);
    mMarketFigureChart.addChildChart(mKLineChartView, 200);

    // 行情图附图(成交量)
    mVolumeView = new KSubChartView(this);
    mMarketFigureChart.addChildChart(mVolumeView, 100);

    // MACD
    mMacdView = new KSubChartView(this);
    mMarketFigureChart.addChildChart(mMacdView, 100);

    // 容器的手势监听
    mMarketFigureChart.setPressChangeListener(this);
  }

  private void initialData(final String json) {
    mProgressBar.setVisibility(View.VISIBLE);
    new Handler().postDelayed(new Runnable() {
      @Override
      public void run() {
        initData(json);
      }
    }, 500);
  }

  private void initViews() {

    mProgressBar = findViewById(R.id.progress_circular);
    RadioGroup radioGroup = findViewById(R.id.rbtn_group);
    radioGroup.setOnCheckedChangeListener(this);
    radioGroup.check(R.id.rbtn_15);
  }

  @Override
  public void onCheckedChanged(RadioGroup group, int checkedId) {
    switch (checkedId) {
      case R.id.rbtn_15:
        initialData("slw_k.json");
        break;
      case R.id.rbtn_1h:
        initialData("geli.json");
        break;
      case R.id.rbtn_4h:
        initialData("maotai.json");
        break;
      case R.id.rbtn_1d:
        initialData("pingan.json");
        break;
    }
  }

  /**
   * 解析行情图数据
   */
  public void initData(String json) {
    // 士兰微k线数据
    String kJson = LocalUtils.getFromAssets(this, json);

    KLineParser parser = new KLineParser(kJson);
    parser.parseKlineData();

    if (mHelper == null) {
      mHelper = new ChartDataSourceHelper(this);
    }
    mProgressBar.setVisibility(View.GONE);
    mHelper.initKDrawData(parser.klineList, mKLineChartView, mVolumeView, mMacdView);
  }

  /**
   * 对主图和附图进行数据填充
   */
  @Override
  public void onReady(List<KLineToDrawItem> data, ExtremeValue extremeValue,
      SubChartData subChartData) {
    mKLineChartView.initData(data, extremeValue,subChartData);
    mVolumeView.initData(data, extremeValue, TechParamType.VOLUME,subChartData);
    mMacdView.initData(data, extremeValue, TechParamType.MACD,subChartData);
  }

  /**
   * 主图的横向滑动
   */
  @Override
  public void onChartTranslate(MotionEvent me, float dX) {
    if (mHelper != null) {
      mHelper.initKMoveDrawData(dX, ChartDataSourceHelper.SourceType.MOVE);
    }
  }

  /**
   * 主图的手势fling
   */
  @Override
  public void onChartFling(float distanceX) {
    if (mHelper != null) {
      mHelper.initKMoveDrawData(distanceX, ChartDataSourceHelper.SourceType.FLING);
    }
  }

  @Override
  public void onChartScale(MotionEvent me, float scaleX, float scaleY) {
    ChartDataSourceHelper.K_D_COLUMNS = (int) (ChartDataSourceHelper.K_D_COLUMNS / scaleX);
    ChartDataSourceHelper.K_D_COLUMNS =
        Math.max(MIN_COLUMNS, Math.min(MAX_COLUMNS, ChartDataSourceHelper.K_D_COLUMNS));
    if (mHelper != null) {
      mHelper.initKMoveDrawData(0, ChartDataSourceHelper.SourceType.SCALE);
    }
  }
}


================================================
FILE: app/src/main/res/drawable/ic_launcher_background.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
  android:width="108dp"
  android:height="108dp"
  android:viewportWidth="108"
  android:viewportHeight="108">
  <path
    android:fillColor="#008577"
    android:pathData="M0,0h108v108h-108z" />
  <path
    android:fillColor="#00000000"
    android:pathData="M9,0L9,108"
    android:strokeWidth="0.8"
    android:strokeColor="#33FFFFFF" />
  <path
    android:fillColor="#00000000"
    android:pathData="M19,0L19,108"
    android:strokeWidth="0.8"
    android:strokeColor="#33FFFFFF" />
  <path
    android:fillColor="#00000000"
    android:pathData="M29,0L29,108"
    android:strokeWidth="0.8"
    android:strokeColor="#33FFFFFF" />
  <path
    android:fillColor="#00000000"
    android:pathData="M39,0L39,108"
    android:strokeWidth="0.8"
    android:strokeColor="#33FFFFFF" />
  <path
    android:fillColor="#00000000"
    android:pathData="M49,0L49,108"
    android:strokeWidth="0.8"
    android:strokeColor="#33FFFFFF" />
  <path
    android:fillColor="#00000000"
    android:pathData="M59,0L59,108"
    android:strokeWidth="0.8"
    android:strokeColor="#33FFFFFF" />
  <path
    android:fillColor="#00000000"
    android:pathData="M69,0L69,108"
    android:strokeWidth="0.8"
    android:strokeColor="#33FFFFFF" />
  <path
    android:fillColor="#00000000"
    android:pathData="M79,0L79,108"
    android:strokeWidth="0.8"
    android:strokeColor="#33FFFFFF" />
  <path
    android:fillColor="#00000000"
    android:pathData="M89,0L89,108"
    android:strokeWidth="0.8"
    android:strokeColor="#33FFFFFF" />
  <path
    android:fillColor="#00000000"
    android:pathData="M99,0L99,108"
    android:strokeWidth="0.8"
    android:strokeColor="#33FFFFFF" />
  <path
    android:fillColor="#00000000"
    android:pathData="M0,9L108,9"
    android:strokeWidth="0.8"
    android:strokeColor="#33FFFFFF" />
  <path
    android:fillColor="#00000000"
    android:pathData="M0,19L108,19"
    android:strokeWidth="0.8"
    android:strokeColor="#33FFFFFF" />
  <path
    android:fillColor="#00000000"
    android:pathData="M0,29L108,29"
    android:strokeWidth="0.8"
    android:strokeColor="#33FFFFFF" />
  <path
    android:fillColor="#00000000"
    android:pathData="M0,39L108,39"
    android:strokeWidth="0.8"
    android:strokeColor="#33FFFFFF" />
  <path
    android:fillColor="#00000000"
    android:pathData="M0,49L108,49"
    android:strokeWidth="0.8"
    android:strokeColor="#33FFFFFF" />
  <path
    android:fillColor="#00000000"
    android:pathData="M0,59L108,59"
    android:strokeWidth="0.8"
    android:strokeColor="#33FFFFFF" />
  <path
    android:fillColor="#00000000"
    android:pathData="M0,69L108,69"
    android:strokeWidth="0.8"
    android:strokeColor="#33FFFFFF" />
  <path
    android:fillColor="#00000000"
    android:pathData="M0,79L108,79"
    android:strokeWidth="0.8"
    android:strokeColor="#33FFFFFF" />
  <path
    android:fillColor="#00000000"
    android:pathData="M0,89L108,89"
    android:strokeWidth="0.8"
    android:strokeColor="#33FFFFFF" />
  <path
    android:fillColor="#00000000"
    android:pathData="M0,99L108,99"
    android:strokeWidth="0.8"
    android:strokeColor="#33FFFFFF" />
  <path
    android:fillColor="#00000000"
    android:pathData="M19,29L89,29"
    android:strokeWidth="0.8"
    android:strokeColor="#33FFFFFF" />
  <path
    android:fillColor="#00000000"
    android:pathData="M19,39L89,39"
    android:strokeWidth="0.8"
    android:strokeColor="#33FFFFFF" />
  <path
    android:fillColor="#00000000"
    android:pathData="M19,49L89,49"
    android:strokeWidth="0.8"
    android:strokeColor="#33FFFFFF" />
  <path
    android:fillColor="#00000000"
    android:pathData="M19,59L89,59"
    android:strokeWidth="0.8"
    android:strokeColor="#33FFFFFF" />
  <path
    android:fillColor="#00000000"
    android:pathData="M19,69L89,69"
    android:strokeWidth="0.8"
    android:strokeColor="#33FFFFFF" />
  <path
    android:fillColor="#00000000"
    android:pathData="M19,79L89,79"
    android:strokeWidth="0.8"
    android:strokeColor="#33FFFFFF" />
  <path
    android:fillColor="#00000000"
    android:pathData="M29,19L29,89"
    android:strokeWidth="0.8"
    android:strokeColor="#33FFFFFF" />
  <path
    android:fillColor="#00000000"
    android:pathData="M39,19L39,89"
    android:strokeWidth="0.8"
    android:strokeColor="#33FFFFFF" />
  <path
    android:fillColor="#00000000"
    android:pathData="M49,19L49,89"
    android:strokeWidth="0.8"
    android:strokeColor="#33FFFFFF" />
  <path
    android:fillColor="#00000000"
    android:pathData="M59,19L59,89"
    android:strokeWidth="0.8"
    android:strokeColor="#33FFFFFF" />
  <path
    android:fillColor="#00000000"
    android:pathData="M69,19L69,89"
    android:strokeWidth="0.8"
    android:strokeColor="#33FFFFFF" />
  <path
    android:fillColor="#00000000"
    android:pathData="M79,19L79,89"
    android:strokeWidth="0.8"
    android:strokeColor="#33FFFFFF" />
</vector>


================================================
FILE: app/src/main/res/drawable/selector_line_bg.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
  <item android:drawable="@android:color/transparent" android:state_checked="false" />

  <item android:state_checked="true">
    <layer-list>
      <item android:width="40dp" android:gravity="center_horizontal" android:left="8dp" android:right="8dp">
        <shape>
          <stroke android:width="2dp" android:color="#4e8bee" />
        </shape>
      </item>
      <item android:bottom="2dp">
        <shape>
          <solid android:color="#ffffff" />

        </shape>
      </item>

    </layer-list>

  </item>


</selector>

================================================
FILE: app/src/main/res/drawable-v24/ic_launcher_foreground.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:aapt="http://schemas.android.com/aapt"
  android:width="108dp"
  android:height="108dp"
  android:viewportWidth="108"
  android:viewportHeight="108">
  <path
    android:fillType="evenOdd"
    android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
    android:strokeWidth="1"
    android:strokeColor="#00000000">
    <aapt:attr name="android:fillColor">
      <gradient
        android:endX="78.5885"
        android:endY="90.9159"
        android:startX="48.7653"
        android:startY="61.0927"
        android:type="linear">
        <item
          android:color="#44000000"
          android:offset="0.0" />
        <item
          android:color="#00000000"
          android:offset="1.0" />
      </gradient>
    </aapt:attr>
  </path>
  <path
    android:fillColor="#FFFFFF"
    android:fillType="nonZero"
    android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
    android:strokeWidth="1"
    android:strokeColor="#00000000" />
</vector>


================================================
FILE: app/src/main/res/layout/activity_main.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:orientation="vertical">

  <include layout="@layout/pankou_layout" />
  <View
    android:layout_width="match_parent"
    android:layout_height="1dp"
    android:background="#e5e5e5" />

  <RadioGroup
    android:id="@+id/rbtn_group"
    android:layout_width="match_parent"
    android:layout_height="40dp"
    android:orientation="horizontal">

    <RadioButton
      android:id="@+id/rbtn_15"
      android:layout_width="0dp"
      android:layout_height="match_parent"
      android:layout_weight="1"
      android:background="@drawable/selector_line_bg"
      android:button="@null"
      android:gravity="center"
      android:text="15分钟" />
    <View
      android:layout_width="0.5dp"
      android:layout_height="match_parent"
      android:background="#cccccc" />
    <RadioButton
      android:id="@+id/rbtn_1h"
      android:layout_width="0dp"
      android:layout_height="match_parent"
      android:layout_weight="1"
      android:background="@drawable/selector_line_bg"
      android:button="@null"
      android:gravity="center"
      android:text="1小时" />
    <View
      android:layout_width="0.5dp"
      android:layout_height="match_parent"
      android:background="#cccccc" />
    <RadioButton
      android:id="@+id/rbtn_4h"
      android:layout_width="0dp"
      android:layout_height="match_parent"
      android:layout_weight="1"
      android:background="@drawable/selector_line_bg"
      android:button="@null"
      android:gravity="center"
      android:text="4小时" />

    <View
      android:layout_width="0.5dp"
      android:layout_height="match_parent"
      android:background="#cccccc" />
    <RadioButton
      android:id="@+id/rbtn_1d"
      android:layout_width="0dp"
      android:layout_height="match_parent"
      android:layout_weight="1"
      android:background="@drawable/selector_line_bg"
      android:button="@null"
      android:gravity="center"
      android:text="1天" />
  </RadioGroup>

  <View
    android:layout_width="match_parent"
    android:layout_height="1dp"
    android:background="#e5e5e5" />

  <FrameLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <com.guannan.chartmodule.chart.MarketFigureChart
      android:id="@+id/chart_container"
      android:layout_width="match_parent"
      android:layout_height="wrap_content" />

    <ProgressBar
      android:id="@+id/progress_circular"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:visibility="gone"
      android:layout_gravity="center"/>

  </FrameLayout>


</LinearLayout>

================================================
FILE: app/src/main/res/layout/pankou_layout.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="100dp"
  android:background="#31465E">

  <TextView
    android:id="@+id/tv_price"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginLeft="10dp"
    android:layout_marginTop="10dp"
    android:text="800.00XXX"
    android:textColor="@color/color_fd4331"
    android:textSize="26sp"
    android:textStyle="bold" />

  <LinearLayout
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_below="@id/tv_price"
    android:layout_marginLeft="10dp"
    android:layout_marginTop="15dp"
    android:orientation="horizontal">

    <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="=4800.00CNY"
      android:textColor="#d2d2d2"
      android:textSize="15sp" />

    <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_marginLeft="20dp"
      android:text="-0.34%"
      android:textColor="@color/color_fd4331"
      android:textSize="15sp" />
  </LinearLayout>

  <LinearLayout
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentRight="true"
    android:layout_marginTop="15dp"
    android:layout_marginRight="10dp"
    android:orientation="vertical">
    <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="高  901.31"
      android:textColor="@color/color_e5e6f2"
      android:textSize="15sp" />

    <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_marginTop="5dp"
      android:text="低  709.55"
      android:textColor="@color/color_e5e6f2"
      android:textSize="15sp" />

    <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_marginTop="5dp"
      android:text="24H  100034"
      android:textColor="@color/color_e5e6f2"
      android:textSize="15sp" />

  </LinearLayout>

</RelativeLayout>


================================================
FILE: app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
  <background android:drawable="@drawable/ic_launcher_background" />
  <foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

================================================
FILE: app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
  <background android:drawable="@drawable/ic_launcher_background" />
  <foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

================================================
FILE: app/src/main/res/values/colors.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>
  <color name="colorPrimary">#008577</color>
  <color name="colorPrimaryDark">#00574B</color>
  <color name="colorAccent">#D81B60</color>
</resources>


================================================
FILE: app/src/main/res/values/strings.xml
================================================
<resources>
  <string name="app_name">StockChart</string>
</resources>


================================================
FILE: app/src/main/res/values/styles.xml
================================================
<resources>

  <!-- Base application theme. -->
  <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    <!-- Customize your theme here. -->
    <item name="colorPrimary">@color/colorPrimary</item>
    <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
    <item name="colorAccent">@color/colorAccent</item>
  </style>

</resources>


================================================
FILE: app/src/test/java/com/guannan/stockchart/ExampleUnitTest.java
================================================
package com.guannan.stockchart;

import org.junit.Test;

import static org.junit.Assert.*;

/**
 * Example local unit test, which will execute on the development machine (host).
 *
 * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
 */
public class ExampleUnitTest {
  @Test
  public void addition_isCorrect() {
    assertEquals(4, 2 + 2);
  }
}

================================================
FILE: build.gradle
================================================
// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
    repositories {
        google()
        jcenter()
        
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.5.2'
        
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        google()
        jcenter()
        
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}


================================================
FILE: chartmodule/.gitignore
================================================
/build


================================================
FILE: chartmodule/build.gradle
================================================
apply plugin: 'com.android.library'

android {
  compileSdkVersion 29
  buildToolsVersion "29.0.2"

  defaultConfig {
    minSdkVersion 19
    targetSdkVersion 29
    versionCode 1
    versionName "1.0"

    testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    consumerProguardFiles 'consumer-rules.pro'
  }

  buildTypes {
    release {
      minifyEnabled false
      proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
    }
  }
}

dependencies {
  implementation fileTree(dir: 'libs', include: ['*.jar'])
  implementation 'androidx.appcompat:appcompat:1.0.2'
  api project(':simulateddata')
}


================================================
FILE: chartmodule/consumer-rules.pro
================================================


================================================
FILE: chartmodule/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
#   http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
#   public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile


================================================
FILE: chartmodule/src/main/AndroidManifest.xml
================================================
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  package="com.guannan.chartmodule" />


================================================
FILE: chartmodule/src/main/java/com/guannan/chartmodule/chart/BaseChartView.java
================================================
package com.guannan.chartmodule.chart;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import com.guannan.chartmodule.R;
import com.guannan.chartmodule.helper.ChartTouchHelper;
import com.guannan.chartmodule.inter.ITouchResponseListener;
import com.guannan.chartmodule.utils.PaintUtils;
import java.lang.ref.WeakReference;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author guannan
 * @date on 2020-02-21 13:28
 * @des 具体绘制View的基类:(1)采用双缓冲绘制机制 (2)支持在子线程当中进行绘制
 */
public abstract class BaseChartView extends View implements ITouchResponseListener {

  /**
   * 如果创建画布Canvas失败:最大重试次数
   */
  private final int MAX_RETRY_COUNT = 3;

  /**
   * 如果创建画布Canvas失败:重试的次数
   */
  private int repeatNum = 0;

  /**
   * 在子线程当中实际进行绘制的canvas
   */
  private Canvas mRealCanvas;

  /**
   * 在子线程当中实际绘制的bitmap
   */
  private Bitmap mRealBitmap;

  /**
   * 用于交换的Bitmap
   */
  private Bitmap mCurBitmap;
  private Bitmap mAlterBitmap;

  /**
   * 绘制完成通知onDraw绘制realBitmap
   */
  private ChartHandler mHandler;

  /**
   * 开始绘制
   */
  private static final int START_PAINT = 1;

  /**
   * 刷新
   */
  private static final int REFRESH = 2;

  /**
   * 绘制mRealBitmap的画笔
   */
  private Paint mPaint;

  /**
   * 是否开始绘制
   */
  private boolean hasDrawed;

  /**
   * 单一线程的线程池
   */
  private ExecutorService mExecutor;

  private DoubleBuffering mDoubleBuffering;

  /**
   * 行情图尺寸等辅助方法
   */
  protected ViewPortHandler mViewPortHandler;

  /**
   * 红色画笔
   */
  protected Paint mPaintRed;

  /**
   * 绿色画笔
   */
  protected Paint mPaintGreen;

  @Override
  protected void onAttachedToWindow() {
    super.onAttachedToWindow();
    initRunnable();
  }

  public BaseChartView(Context context) {
    this(context, null);
  }

  public BaseChartView(Context context,
      @Nullable AttributeSet attrs) {
    this(context, attrs, 0);
  }

  public BaseChartView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);

    mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

    mPaintRed = new Paint();
    mPaintRed.setColor(ContextCompat.getColor(context, R.color.color_fd4331));
    mPaintRed.setStyle(Paint.Style.FILL);

    mPaintGreen = new Paint();
    mPaintGreen.setColor(ContextCompat.getColor(context, R.color.color_05aa3b));
    mPaintGreen.setStyle(Paint.Style.FILL);

    // 行情图尺寸等辅助方法
    mViewPortHandler = new ViewPortHandler();

    initHandler();

    PaintUtils.init(context);
  }

  private void initRunnable() {
    if (mExecutor == null) {
      mExecutor = Executors.newSingleThreadExecutor();
    }
    if (mDoubleBuffering == null) {
      mDoubleBuffering = new DoubleBuffering(this);
    }
  }

  private void initHandler() {
    mHandler = new ChartHandler(this);
  }

  public abstract void onChartLongPressed(MotionEvent me);

  public abstract void onChartGestureEnd(MotionEvent me,
      ChartTouchHelper.ChartGesture lastPerformedGesture);

  public abstract void onChartSingleTapped(MotionEvent me);

  /**
   * 防止内存泄露的Handler
   */
  private static final class ChartHandler extends Handler {

    private final WeakReference<BaseChartView> mView;

    public ChartHandler(BaseChartView view) {
      mView = new WeakReference<>(view);
    }

    @Override
    public void handleMessage(@NonNull Message msg) {
      super.handleMessage(msg);
      if (mView != null) {
        BaseChartView chartView = mView.get();
        if (chartView != null) {
          if (msg.what == chartView.START_PAINT) {
            // 开始绘制
            DoubleBuffering doubleBuffering = (DoubleBuffering) msg.obj;
            if (chartView.mExecutor != null) {
              chartView.mExecutor.execute(doubleBuffering);
            }
            //post(doubleBuffering);
          } else if (msg.what == chartView.REFRESH) {

            if (chartView.mRealBitmap == null) {
              chartView.mRealBitmap = chartView.mCurBitmap;
              chartView.mCurBitmap = chartView.mAlterBitmap;
            } else {
              Bitmap alterBitmap = chartView.mRealBitmap;
              chartView.mRealBitmap = chartView.mCurBitmap;
              chartView.mCurBitmap = alterBitmap;
            }
            chartView.mRealCanvas.setBitmap(chartView.mCurBitmap);
            chartView.invalidate();
          }
        }
      }
    }
  }

  @Override
  protected void onSizeChanged(int w, int h, int oldw, int oldh) {

    mViewPortHandler.setChartDimens(w, h);

    initCanvas();
    super.onSizeChanged(w, h, oldw, oldh);
  }

  /**
   * 根据当前View的尺寸创建画布
   */
  private void initCanvas() {
    repeatNum = 0;
    if (mRealCanvas == null) {
      mRealCanvas = new Canvas();

      Bitmap curBitmap =
          createBitmap(mViewPortHandler.getChartWidth(), mViewPortHandler.getChartHeight(),
              Bitmap.Config.ARGB_8888);
      Bitmap alterBitmap = curBitmap.copy(Bitmap.Config.ARGB_8888, true);
      if (curBitmap != null && alterBitmap != null) {
        mRealCanvas.setBitmap(curBitmap);
        mCurBitmap = curBitmap;
        mAlterBitmap = alterBitmap;
      }
    }
  }

  /**
   * 根据当前View的宽高创建画布Canvas
   */
  private Bitmap createBitmap(int width, int height, Bitmap.Config config) {
    Bitmap bitmap = null;
    try {
      repeatNum++;
      if (repeatNum < MAX_RETRY_COUNT) {
        bitmap = Bitmap.createBitmap(width, height, config);
      }
    } catch (OutOfMemoryError e) {
      System.gc();
      System.runFinalization();
      bitmap = createBitmap(width, height, config);
    }
    return bitmap;
  }

  @Override
  protected void onDetachedFromWindow() {
    super.onDetachedFromWindow();
    if (mHandler != null) {
      mHandler.removeCallbacksAndMessages(null);
    }
    if (mExecutor != null) {
      mExecutor.shutdown();
    }
    if (mRealBitmap != null) {
      if (!mRealBitmap.isRecycled()) {
        mRealBitmap.recycle();
      }
      mRealBitmap = null;
    }

    if (mCurBitmap != null) {
      if (!mCurBitmap.isRecycled()) {
        mCurBitmap.recycle();
      }
      mCurBitmap = null;
    }

    if (mAlterBitmap != null) {
      if (!mAlterBitmap.isRecycled()) {
        mAlterBitmap.recycle();
      }
      mAlterBitmap = null;
    }
  }

  /**
   * 在子线程当中进行绘制
   */
  class DoubleBuffering implements Runnable {

    private final WeakReference<BaseChartView> mChartView;

    public DoubleBuffering(BaseChartView view) {
      mChartView = new WeakReference<>(view);
    }

    @Override
    public synchronized void run() {
      if (mChartView != null) {
        BaseChartView baseChartView = mChartView.get();
        if (baseChartView != null && baseChartView.mRealCanvas != null) {
          baseChartView.drawFrame(baseChartView.mRealCanvas);
          // 绘制完成,通知UI线程绘制mRealBitmap
          Bitmap bitmap = baseChartView.mCurBitmap;
          if (bitmap != null && baseChartView.mHandler != null) {
            baseChartView.mHandler.sendEmptyMessage(baseChartView.REFRESH);
          }
        }
      }
    }
  }

  /**
   * 子类View继承,真正实现绘制,当前线程在子线程
   */
  protected void drawFrame(Canvas canvas) {
    canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
  }

  /**
   * 子类决定刷新视图时机
   */
  public void invalidateView() {
    hasDrawed = true;
    invalidate();
  }

  public String getString(int id) {
    return getContext().getResources().getString(id);
  }

  /**
   * 在子线程当中刷新
   */
  public void postInvalidateView() {
    hasDrawed = true;
    postInvalidate();
  }

  @Override
  protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    if (mRealBitmap != null) {
      canvas.drawBitmap(mRealBitmap, 0, 0, mPaint);
    }
    if (hasDrawed) {
      hasDrawed = false;
      if (!mHandler.hasMessages(START_PAINT)) {
        Message message = new Message();
        message.what = START_PAINT;
        message.obj = mDoubleBuffering;
        mHandler.sendMessage(message);
      }
    }
  }
}


================================================
FILE: chartmodule/src/main/java/com/guannan/chartmodule/chart/KMasterChartView.java
================================================
package com.guannan.chartmodule.chart;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.MotionEvent;
import androidx.annotation.Nullable;
import com.guannan.chartmodule.R;
import com.guannan.chartmodule.data.ExtremeValue;
import com.guannan.chartmodule.data.KLineToDrawItem;
import com.guannan.chartmodule.data.SubChartData;
import com.guannan.chartmodule.data.TechItem;
import com.guannan.chartmodule.helper.ChartDataSourceHelper;
import com.guannan.chartmodule.helper.ChartTouchHelper;
import com.guannan.chartmodule.utils.DateUtils;
import com.guannan.chartmodule.utils.DisplayUtils;
import com.guannan.chartmodule.utils.NumFormatUtils;
import com.guannan.chartmodule.utils.PaintUtils;
import com.guannan.simulateddata.entity.KLineItem;
import java.util.List;

/**
 * @author guannan
 * @date on 2020-03-07 15:56
 * @des K线的主图
 */
public class KMasterChartView extends BaseChartView {

  /**
   * 绘制的蜡烛线数据(主副图的数据)
   */
  private List<KLineToDrawItem> mToDrawList;

  /**
   * 蜡烛线价格最大最小值
   */
  private ExtremeValue mExtremeValue;

  /**
   * 主图文本间隔
   */
  private int TEXT_PADDING;

  /**
   * 刻度间隔
   */
  private int CAL_PADDING;

  /**
   * 十字线长按选中的点
   */
  private int mFocusIndex;

  /**
   * 长按十字线的位置
   */
  private PointF mFocusPoint;

  /**
   * 是否是长按
   */
  private boolean onLongPress;

  /**
   * 长按弹出的弹框
   */
  private RectF popRect = new RectF();

  private SubChartData mSubChartData;

  public KMasterChartView(Context context) {
    this(context, null);
  }

  public KMasterChartView(Context context,
      @Nullable AttributeSet attrs) {
    this(context, attrs, 0);
  }

  public KMasterChartView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);

    TEXT_PADDING = DisplayUtils.dip2px(context, 5);
    CAL_PADDING = DisplayUtils.dip2px(context, 3);

    mViewPortHandler.setContentRatio(0.9f);
  }

  @Override
  protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    mViewPortHandler.restrainViewPort(DisplayUtils.dip2px(getContext(), 10),
        DisplayUtils.dip2px(getContext(), 20), DisplayUtils.dip2px(getContext(), 10),
        0);
    super.onSizeChanged(w, h, oldw, oldh);
  }

  public ViewPortHandler getViewPortHandler() {
    return mViewPortHandler;
  }

  @Override
  protected void drawFrame(Canvas canvas) {
    super.drawFrame(canvas);
    drawOutLine(canvas);
    if (mToDrawList == null || mToDrawList.isEmpty()) {
      return;
    }
    RectF contentRect = mViewPortHandler.mContentRect;
    for (int i = 0; i < mToDrawList.size(); i++) {
      KLineToDrawItem drawItem = mToDrawList.get(i);
      if (drawItem != null) {
        // 绘制蜡烛线日期(只绘制每月第一个交易日)
        if (!TextUtils.isEmpty(drawItem.date)) {
          Rect rect = new Rect();
          PaintUtils.TEXT_PAINT.getTextBounds(drawItem.date, 0, drawItem.date.length(), rect);
          canvas.drawText(drawItem.date, drawItem.rect.centerX() - rect.width() / 2,
              contentRect.bottom + rect.height() + TEXT_PADDING,
              PaintUtils.TEXT_PAINT);
          canvas.drawLine(drawItem.rect.centerX(), contentRect.top, drawItem.rect.centerX(),
              contentRect.bottom, PaintUtils.GRID_INNER_DIVIDER);
        }
        // 绘制蜡烛线
        if (drawItem.isFall) {
          canvas.drawRect(drawItem.rect, mPaintGreen);
          canvas.drawRect(drawItem.shadowRect, mPaintGreen);
        } else {
          canvas.drawRect(drawItem.rect, mPaintRed);
          canvas.drawRect(drawItem.shadowRect, mPaintRed);
        }
      }
    }

    // 绘制BOLL(布林指标)
    if (!onLongPress) {
      KLineToDrawItem lastItem = mToDrawList.get(mToDrawList.size() - 1);
      drawBollDes(canvas, contentRect, lastItem);
    }

    canvas.drawPath(mSubChartData.bollPaths[0], PaintUtils.LINE_BLUE_PAINT);
    canvas.drawPath(mSubChartData.bollPaths[1], PaintUtils.LINE_PURPLE_PAINT);
    canvas.drawPath(mSubChartData.bollPaths[2], PaintUtils.LINE_YELLOW_PAINT);

    // 绘制长按十字线
    if (mFocusPoint != null && onLongPress) {
      if (contentRect.contains(mFocusPoint.x, mFocusPoint.y)) {
        canvas.drawLine(contentRect.left, mFocusPoint.y, contentRect.right, mFocusPoint.y,
            PaintUtils.FOCUS_LINE_PAINT);
      }
      canvas.drawLine(mFocusPoint.x, contentRect.top, mFocusPoint.x, contentRect.bottom,
          PaintUtils.FOCUS_LINE_PAINT);
      KLineToDrawItem item = mToDrawList.get(mFocusIndex);
      drawBollDes(canvas, contentRect, item);
    }

    // 长按显示的弹框
    showLongPressDialog(canvas, contentRect);
  }

  /**
   * 绘制主图左上角默认显示和长按BOLL指标展示
   */
  private void drawBollDes(Canvas canvas, RectF contentRect, KLineToDrawItem drawItem) {
    TechItem techItem = drawItem.techItem;
    float mid = NumFormatUtils.formatFloat((techItem.upper + techItem.lower) / 2, 2);
    String midDes = "BOLL  MID:" + mid;
    Rect rectMid = new Rect();
    PaintUtils.TEXT_YELLOW_PAINT.getTextBounds(midDes, 0, midDes.length(), rectMid);
    canvas.drawText(midDes, contentRect.left, contentRect.top - TEXT_PADDING,
        PaintUtils.TEXT_YELLOW_PAINT);

    float upper = NumFormatUtils.formatFloat(techItem.upper, 2);
    String upperDes = "UPPER:" + upper;
    Rect rectUpper = new Rect();
    PaintUtils.TEXT_BLUE_PAINT.getTextBounds(upperDes, 0, upperDes.length(), rectUpper);
    canvas.drawText(upperDes, contentRect.left + rectMid.width() + TEXT_PADDING,
        contentRect.top - TEXT_PADDING, PaintUtils.TEXT_BLUE_PAINT);

    float lower = NumFormatUtils.formatFloat(techItem.lower, 2);
    String lowerDes = "LOWER:" + lower;
    canvas.drawText(lowerDes,
        contentRect.left + rectMid.width() + rectUpper.width() + TEXT_PADDING * 2,
        contentRect.top - TEXT_PADDING, PaintUtils.TEXT_PURPLE_PAINT);
  }

  /**
   * 长按显示的弹框内容
   */
  private void showLongPressDialog(Canvas canvas, RectF contentRect) {
    if (onLongPress) {
      KLineToDrawItem item = mToDrawList.get(mFocusIndex);
      float left = contentRect.right - TEXT_PADDING * 2 - DisplayUtils.dip2px(getContext(), 110);
      float top = contentRect.top + TEXT_PADDING;
      popRect.set(left, top,
          left + DisplayUtils.dip2px(getContext(), 110),
          top + DisplayUtils.dip2px(getContext(), 120));
      canvas.drawRect(popRect, PaintUtils.POP_DIALOG_PAINT);

      KLineItem klineItem = item.klineItem;
      float perHeight = popRect.height() / 7;

      drawPopText(canvas, popRect, getString(R.string.date), DateUtils.getYMD(klineItem.day),
          item.isFall, perHeight);
      drawPopText(canvas, popRect, getString(R.string.open), String.valueOf(klineItem.open),
          item.isFall,
          perHeight * 2);
      drawPopText(canvas, popRect, getString(R.string.hign), String.valueOf(klineItem.high),
          item.isFall,
          perHeight * 3);
      drawPopText(canvas, popRect, getString(R.string.low), String.valueOf(klineItem.low),
          item.isFall, perHeight * 4);
      drawPopText(canvas, popRect, getString(R.string.close), String.valueOf(klineItem.close),
          item.isFall,
          perHeight * 5);
      drawPopText(canvas, popRect, getString(R.string.diff),
          String.valueOf(NumFormatUtils.formatFloat(klineItem.open - klineItem.preClose, 2)),
          item.isFall,
          perHeight * 6);
      float chg = (klineItem.open - klineItem.preClose) * 100 / klineItem.preClose;
      String chgDesc = NumFormatUtils.formatFloat(chg, 2, true, true, "--", "--", false);
      drawPopText(canvas, popRect, getString(R.string.chg), chgDesc, item.isFall, perHeight * 7);
    }
  }

  public void drawPopText(Canvas canvas, RectF popRect, String title, String value, boolean isFall,
      float y) {
    Rect rect = new Rect();
    PaintUtils.TEXT_POP_PAINT.getTextBounds(value, 0, value.length(), rect);
    canvas.drawText(title, popRect.left + TEXT_PADDING, popRect.top + y - TEXT_PADDING,
        PaintUtils.TEXT_POP_PAINT);
    if (title.equals(getString(R.string.diff)) || title.equals(getString(R.string.chg))) {
      if (isFall) {
        canvas.drawText(value, popRect.right - rect.width() - TEXT_PADDING,
            popRect.top + y - TEXT_PADDING,
            PaintUtils.TEXT_GREEN_PAINT);
      } else {
        canvas.drawText(value, popRect.right - rect.width() - TEXT_PADDING,
            popRect.top + y - TEXT_PADDING,
            PaintUtils.TEXT_RED_PAINT);
      }
    } else {
      canvas.drawText(value, popRect.right - rect.width() - TEXT_PADDING,
          popRect.top + y - TEXT_PADDING,
          PaintUtils.TEXT_POP_PAINT);
    }
  }

  /**
   * 绘制行情图边框
   */
  private void drawOutLine(Canvas canvas) {
    if (mExtremeValue == null) {
      return;
    }
    RectF contentRect = mViewPortHandler.mContentRect;
    if (contentRect != null) {

      Path path = new Path();
      path.moveTo(contentRect.left, contentRect.top);
      path.lineTo(contentRect.right, contentRect.top);
      path.lineTo(contentRect.right, contentRect.bottom);
      path.lineTo(contentRect.left, contentRect.bottom);
      path.close();
      canvas.drawPath(path, PaintUtils.GRID_DIVIDER);
    }

    // 绘制价格刻度和价格分隔线
    float maxPrice = NumFormatUtils.formatFloat(mExtremeValue.maxPrice, 2);
    float minPrice = NumFormatUtils.formatFloat(mExtremeValue.minPrice, 2);
    Rect rect = new Rect();
    PaintUtils.TEXT_PAINT.getTextBounds(maxPrice + "", 0, String.valueOf(maxPrice).length(), rect);
    canvas.drawText(maxPrice + "", contentRect.left + CAL_PADDING,
        contentRect.top + rect.height() + CAL_PADDING,
        PaintUtils.TEXT_PAINT);
    float perHeight = contentRect.height() / 4;
    float perPrice = NumFormatUtils.formatFloat((maxPrice - minPrice) / 4, 2);

    for (int i = 1; i <= 3; i++) {
      canvas.drawLine(contentRect.left, contentRect.top + perHeight * i, contentRect.right,
          contentRect.top + perHeight * i, PaintUtils.GRID_INNER_DIVIDER);
      float value = NumFormatUtils.formatFloat(maxPrice - perPrice * i, 2);
      canvas.drawText(value + "", contentRect.left + CAL_PADDING,
          contentRect.top + perHeight * i - CAL_PADDING, PaintUtils.TEXT_PAINT);
    }

    canvas.drawText(minPrice + "", contentRect.left + CAL_PADDING, contentRect.bottom - CAL_PADDING,
        PaintUtils.TEXT_PAINT);
  }

  /**
   * 设置主图数据并触发绘制
   */
  public void initData(List<KLineToDrawItem> klineList, ExtremeValue extremeValue,
      SubChartData subChartData) {
    this.mToDrawList = klineList;
    this.mExtremeValue = extremeValue;
    this.mSubChartData = subChartData;
    invalidateView();
  }

  /**
   * 长按
   */
  @Override
  public void onChartLongPressed(MotionEvent me) {

    onLongPress = true;
    mFocusPoint = new PointF();
    mFocusPoint.set(me.getX(), me.getY());

    RectF contentRect = mViewPortHandler.mContentRect;

    if (contentRect == null || contentRect.width() <= 0) {
      return;
    }
    mFocusIndex = (int) ((mFocusPoint.x - contentRect.left) * ChartDataSourceHelper.K_D_COLUMNS
        / contentRect.width());
    mFocusIndex = Math.max(0, Math.min(mFocusIndex, ChartDataSourceHelper.K_D_COLUMNS - 1));

    invalidateView();
  }

  @Override
  public void onChartGestureEnd(MotionEvent me,
      ChartTouchHelper.ChartGesture lastPerformedGesture) {
    if (lastPerformedGesture == ChartTouchHelper.ChartGesture.LONG_PRESS) {
      if (mFocusPoint != null) {
        mFocusPoint.set(me.getX(), me.getY());
      }
      onLongPress = false;
    }
    invalidateView();
  }

  @Override
  public void onChartSingleTapped(MotionEvent me) {

  }
}


================================================
FILE: chartmodule/src/main/java/com/guannan/chartmodule/chart/KSubChartView.java
================================================
package com.guannan.chartmodule.chart;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.MotionEvent;
import androidx.annotation.Nullable;
import com.guannan.chartmodule.data.ExtremeValue;
import com.guannan.chartmodule.data.KLineToDrawItem;
import com.guannan.chartmodule.data.LineRectItem;
import com.guannan.chartmodule.data.SubChartData;
import com.guannan.chartmodule.data.TechItem;
import com.guannan.chartmodule.helper.ChartDataSourceHelper;
import com.guannan.chartmodule.helper.ChartTouchHelper;
import com.guannan.chartmodule.helper.TechParamType;
import com.guannan.chartmodule.utils.DisplayUtils;
import com.guannan.chartmodule.utils.NumFormatUtils;
import com.guannan.chartmodule.utils.PaintUtils;
import java.util.List;

/**
 * @author guannan
 * @date on 2020-04-19 14:23
 * @des K线的副图
 */
public class KSubChartView extends BaseChartView {

  /**
   * 绘制的蜡烛线数据(主副图的数据)
   */
  private List<KLineToDrawItem> mToDrawList;

  /**
   * 最大值最小值
   */
  private ExtremeValue mExtremeValue;

  private SubChartData mSubChartData;

  /**
   * 主图文本间隔
   */
  private int TEXT_PADDING;

  /**
   * 十字线长按选中的点
   */
  private int mFocusIndex;

  /**
   * 长按十字线的位置
   */
  private PointF mFocusPoint;

  /**
   * 手指是否长按
   */
  private boolean onLongPress;

  /**
   * 附图类型
   */
  private TechParamType mTechParamType;

  public KSubChartView(Context context) {
    this(context, null);
  }

  public KSubChartView(Context context, @Nullable AttributeSet attrs) {
    this(context, attrs, 0);
  }

  public KSubChartView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);

    TEXT_PADDING = DisplayUtils.dip2px(context, 5);
  }

  @Override
  protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    mViewPortHandler.restrainViewPort(DisplayUtils.dip2px(getContext(), 10),
        DisplayUtils.dip2px(getContext(), 15), DisplayUtils.dip2px(getContext(), 10),
        0);
    super.onSizeChanged(w, h, oldw, oldh);
  }

  public ViewPortHandler getViewPortHandler() {
    return mViewPortHandler;
  }

  @Override
  protected void drawFrame(Canvas canvas) {
    super.drawFrame(canvas);
    if (mToDrawList == null || mToDrawList.isEmpty()) {
      return;
    }

    // 绘制边框和刻度
    drawOutLine(canvas);

    RectF contentRect = mViewPortHandler.mContentRect;

    KLineToDrawItem item = mToDrawList.get(mToDrawList.size() - 1);
    if (mTechParamType == TechParamType.VOLUME) {
      drawVolume(canvas, contentRect);
      if (!onLongPress) {
        drawVolumeDes(canvas, contentRect, item);
      }
    } else if (mTechParamType == TechParamType.MACD) {

      drawMacd(canvas, contentRect);
      if (!onLongPress) {
        drawTechDes(canvas, contentRect, item);
      }
    }

    if (mFocusPoint != null && onLongPress) {

      // 附图实际y轴位置
      float focusY = mFocusPoint.y - getY() - contentRect.top;

      if (contentRect.contains(mFocusPoint.x, focusY)) {
        canvas.drawLine(contentRect.left, focusY, contentRect.right, focusY,
            PaintUtils.FOCUS_LINE_PAINT);
      }
      canvas.drawLine(mFocusPoint.x, contentRect.top, mFocusPoint.x, contentRect.bottom,
          PaintUtils.FOCUS_LINE_PAINT);
      KLineToDrawItem drawItem = mToDrawList.get(mFocusIndex);
      if (mTechParamType == TechParamType.MACD) {
        drawTechDes(canvas, contentRect, drawItem);
      } else if (mTechParamType == TechParamType.VOLUME) {
        drawVolumeDes(canvas, contentRect, drawItem);
      }
    }
  }

  /**
   * 绘制成交量左上角指标
   */
  private void drawVolumeDes(Canvas canvas, RectF contentRect, KLineToDrawItem item) {
    long volume = item.klineItem.volume;
    String volumeDes = "成交量:" + NumFormatUtils.formatBigFloatAll(volume, 2);
    canvas.drawText(volumeDes, contentRect.left, contentRect.top - TEXT_PADDING,
        PaintUtils.TEXT_PAINT);
  }

  /**
   * 绘制MACD左上角指标显示
   */
  public void drawTechDes(Canvas canvas, RectF contentRect, KLineToDrawItem drawItem) {

    TechItem techItem = drawItem.techItem;
    float dif = NumFormatUtils.formatFloat(techItem.dif, 2);
    String difDes = "MACD  DIF:" + dif;
    Rect rectMid = new Rect();
    PaintUtils.TEXT_YELLOW_PAINT.getTextBounds(difDes, 0, difDes.length(), rectMid);
    canvas.drawText(difDes, contentRect.left, contentRect.top - TEXT_PADDING,
        PaintUtils.TEXT_YELLOW_PAINT);

    float dea = NumFormatUtils.formatFloat(techItem.dea, 2);
    String deaDes = "DEA:" + dea;
    Rect rectUpper = new Rect();
    PaintUtils.TEXT_BLUE_PAINT.getTextBounds(deaDes, 0, deaDes.length(), rectUpper);
    canvas.drawText(deaDes, contentRect.left + rectMid.width() + TEXT_PADDING,
        contentRect.top - TEXT_PADDING, PaintUtils.TEXT_BLUE_PAINT);

    float macd = NumFormatUtils.formatFloat(techItem.macd, 2);
    String macdDes = "MACD:" + macd;
    canvas.drawText(macdDes,
        contentRect.left + rectMid.width() + rectUpper.width() + TEXT_PADDING * 2,
        contentRect.top - TEXT_PADDING, PaintUtils.TEXT_PURPLE_PAINT);
  }

  /**
   * 绘制附图MACD
   */
  private void drawMacd(Canvas canvas, RectF contentRect) {
    if (mSubChartData != null) {
      canvas.drawPath(mSubChartData.macdPaths[0], PaintUtils.LINE_BLUE_PAINT);
      canvas.drawPath(mSubChartData.macdPaths[1], PaintUtils.LINE_YELLOW_PAINT);
      List<LineRectItem> macdRects = mSubChartData.macdRects;
      if (macdRects != null && !macdRects.isEmpty()) {
        for (int i = 0; i < macdRects.size(); i++) {
          LineRectItem lineRectItem = macdRects.get(i);
          if (lineRectItem != null) {
            boolean isFall = lineRectItem.isFall;
            if (isFall) {
              canvas.drawRect(lineRectItem.rect, mPaintGreen);
            } else {
              canvas.drawRect(lineRectItem.rect, mPaintRed);
            }
          }
        }
      }
    }
  }

  /**
   * 绘制成交量
   */
  private void drawVolume(Canvas canvas, RectF contentRect) {
    for (int i = 0; i < mToDrawList.size(); i++) {
      KLineToDrawItem drawItem = mToDrawList.get(i);
      if (drawItem != null) {
        // 绘制内部分隔线
        if (!TextUtils.isEmpty(drawItem.date)) {
          canvas.drawLine(drawItem.rect.centerX(), contentRect.top, drawItem.rect.centerX(),
              contentRect.bottom, PaintUtils.GRID_INNER_DIVIDER);
        }
        // 绘制成交量柱
        if (drawItem.isFall) {
          canvas.drawRect(drawItem.volumeRect, mPaintGreen);
        } else {
          canvas.drawRect(drawItem.volumeRect, mPaintRed);
        }
      }
    }
  }

  /**
   * 绘制外围边框和刻度
   */
  private void drawOutLine(Canvas canvas) {
    RectF contentRect = mViewPortHandler.mContentRect;
    if (contentRect != null) {
      Path path = new Path();
      path.moveTo(contentRect.left, contentRect.top);
      path.lineTo(contentRect.right, contentRect.top);
      path.lineTo(contentRect.right, contentRect.bottom);
      path.lineTo(contentRect.left, contentRect.bottom);
      path.close();
      canvas.drawPath(path, PaintUtils.GRID_DIVIDER);
    }
    canvas.drawLine(contentRect.left, contentRect.centerY(), contentRect.right,
        contentRect.centerY(), PaintUtils.GRID_INNER_DIVIDER);
    // 绘制附图最大刻度
    if (mExtremeValue != null && mTechParamType == TechParamType.VOLUME) {
      String maxVolume = NumFormatUtils.formatBigFloatAll(mExtremeValue.maxVolume, 2);
      Rect rect = new Rect();
      PaintUtils.TEXT_PAINT.getTextBounds(maxVolume, 0, maxVolume.length(), rect);
      canvas.drawText(maxVolume, contentRect.left + TEXT_PADDING,
          contentRect.top + rect.height() + TEXT_PADDING,
          PaintUtils.TEXT_PAINT);
    }
  }

  /**
   * 设置副图数据,并触发绘制
   */
  public void initData(List<KLineToDrawItem> data, ExtremeValue extremeValue,
      TechParamType techParamType, SubChartData subChartData) {
    this.mToDrawList = data;
    this.mExtremeValue = extremeValue;
    this.mTechParamType = techParamType;
    this.mSubChartData = subChartData;
    invalidateView();
  }

  /**
   * 长按
   */
  @Override
  public void onChartLongPressed(MotionEvent me) {

    onLongPress = true;
    mFocusPoint = new PointF();
    mFocusPoint.set(me.getX(), me.getY());

    RectF contentRect = mViewPortHandler.mContentRect;

    if (contentRect == null || contentRect.width() <= 0) {
      return;
    }
    mFocusIndex = (int) ((mFocusPoint.x - contentRect.left) * ChartDataSourceHelper.K_D_COLUMNS
        / contentRect.width());
    mFocusIndex = Math.max(0, Math.min(mFocusIndex, ChartDataSourceHelper.K_D_COLUMNS - 1));

    invalidateView();
  }

  @Override
  public void onChartGestureEnd(MotionEvent me,
      ChartTouchHelper.ChartGesture lastPerformedGesture) {
    if (lastPerformedGesture == ChartTouchHelper.ChartGesture.LONG_PRESS) {
      if (mFocusPoint != null) {
        mFocusPoint.set(me.getX(), me.getY());
      }
      onLongPress = false;
    }
    invalidateView();
  }

  @Override
  public void onChartSingleTapped(MotionEvent me) {
    //if (mTechParamType == TechParamType.VOLUME) {
    //  mTechParamType = TechParamType.MACD;
    //} else if (mTechParamType == TechParamType.MACD) {
    //  mTechParamType = TechParamType.VOLUME;
    //}
    //invalidateView();
  }
}


================================================
FILE: chartmodule/src/main/java/com/guannan/chartmodule/chart/MarketFigureChart.java
================================================
package com.guannan.chartmodule.chart;

import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import androidx.annotation.Nullable;
import com.guannan.chartmodule.helper.ChartTouchHelper;
import com.guannan.chartmodule.inter.IChartGestureListener;
import com.guannan.chartmodule.inter.IPressChangeListener;
import com.guannan.chartmodule.utils.DisplayUtils;

/**
 * @author guannan
 * @date on 2020-04-21 11:54
 * @des 行情图容器
 * #######################(1)动态添加主图和副图 ########################
 * #######################(2)处理主图和副图的手势 ######################
 */
public class MarketFigureChart extends LinearLayout implements IChartGestureListener {

  private Context mContext;

  private IPressChangeListener mPressChangeListener;

  public MarketFigureChart(Context context) {
    this(context, null);
  }

  public MarketFigureChart(Context context,
      @Nullable AttributeSet attrs) {
    this(context, attrs, 0);
  }

  public MarketFigureChart(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    this.mContext = context;
    setOrientation(VERTICAL);
    setLayoutParams(
        new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
    // 设置容器的手势监听
    ChartTouchHelper chartTouchHelper = new ChartTouchHelper(this);
    chartTouchHelper.setChartGestureListener(this);
  }

  /**
   * 向容器中添加主图、附图视图
   */
  public void addChildChart(BaseChartView childView, float height) {
    LinearLayout.LayoutParams params =
        new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,
            DisplayUtils.dip2px(mContext, height));
    addView(childView, params);
  }

  public void setPressChangeListener(IPressChangeListener listener) {
    this.mPressChangeListener = listener;
  }

  @Override
  public void onChartGestureStart(MotionEvent me,
      ChartTouchHelper.ChartGesture lastPerformedGesture) {

  }

  @Override
  public void onChartGestureEnd(MotionEvent me,
      ChartTouchHelper.ChartGesture lastPerformedGesture) {
    for (int i = 0; i < getChildCount(); i++) {
      BaseChartView baseChartView = (BaseChartView) getChildAt(i);
      if (baseChartView != null) {
        baseChartView.onChartGestureEnd(me, lastPerformedGesture);
      }
    }
  }

  @Override
  public void onChartLongPressed(MotionEvent me) {
    for (int i = 0; i < getChildCount(); i++) {
      BaseChartView baseChartView = (BaseChartView) getChildAt(i);
      if (baseChartView != null) {
        baseChartView.onChartLongPressed(me);
      }
    }
  }

  @Override
  public void onChartDoubleTapped(MotionEvent me) {

  }

  @Override
  public void onChartSingleTapped(MotionEvent me) {
    for (int i = 0; i < getChildCount(); i++) {
      BaseChartView baseChartView = (BaseChartView) getChildAt(i);
      if (baseChartView != null) {
        baseChartView.onChartSingleTapped(me);
      }
    }
  }

  @Override
  public void onChartFling(float distanceX) {
    if (mPressChangeListener != null) {
      mPressChangeListener.onChartFling(distanceX);
    }
  }

  @Override
  public void onChartScale(MotionEvent me, float scaleX, float scaleY) {
    if (mPressChangeListener != null) {
      mPressChangeListener.onChartScale(me, scaleX, scaleY);
    }
  }

  @Override
  public void onChartTranslate(MotionEvent me, float dX) {
    if (mPressChangeListener != null) {
      mPressChangeListener.onChartTranslate(me, dX);
    }
  }
}


================================================
FILE: chartmodule/src/main/java/com/guannan/chartmodule/chart/ViewPortHandler.java
================================================
package com.guannan.chartmodule.chart;

import android.graphics.RectF;

/**
 * @author guannan
 * @date on 2020-03-29 15:33
 * @des 行情图尺寸相关辅助方法
 */
public class ViewPortHandler {

  /**
   * 画布的宽高
   */
  private int mWidth, mHeight;

  /**
   * 绘制区域
   */
  public RectF mContentRect = new RectF();

  /**
   * 主图蜡烛线区域所占比例
   */
  private float ratio = 1;

  public ViewPortHandler() {

  }

  /**
   * 设置行情图的尺寸
   */
  public void setChartDimens(int width, int height) {

    float left = offLeft();
    float top = offTop();
    float right = offRight();
    float bottom = offBottom();

    this.mWidth = width;
    this.mHeight = height;
    restrainViewPort(left, top, right, bottom);
  }

  /**
   * 绘制日期区域所占比例
   */
  public void setContentRatio(float ratio) {
    this.ratio = ratio;
  }

  /**
   * 重置绘制区域的大小
   */
  public void restrainViewPort(float offLeft, float offTop, float offRight, float offBottom) {
    // 绘制的内容区域的大小
    mContentRect.set(offLeft, offTop, mWidth - offRight, mHeight * ratio - offBottom);
  }

  /**
   * 图表的宽度
   */
  public int getChartWidth() {
    return mWidth;
  }

  /**
   * 图表的高度
   */
  public int getChartHeight() {
    return mHeight;
  }

  /**
   * 左间距
   */
  public float offLeft() {
    return mContentRect.left;
  }

  /**
   * 上间距
   */
  public float offTop() {
    return mContentRect.top;
  }

  /**
   * 右间距
   */
  public float offRight() {
    return mWidth - mContentRect.right;
  }

  /**
   * 底部间距
   */
  public float offBottom() {
    return mContentRect.bottom;
  }
}


================================================
FILE: chartmodule/src/main/java/com/guannan/chartmodule/data/ExtremeValue.java
================================================
package com.guannan.chartmodule.data;

/**
 * @author guannan
 * @date on 2020-04-25 14:10
 * @des 最大值、最小值
 */
public class ExtremeValue {

  /**
   * 蜡烛线最大价格
   */
  public float maxPrice;

  /**
   * 蜡烛线最小价格
   */
  public float minPrice;

  /**
   * 蜡烛线最大成交量
   */
  public float maxVolume;

  /**
   * MACD最大值
   */
  public float macdMax;

  /**
   * MACD最小值
   */
  public float macdMin;

  /**
   * Boll最大值
   */
  public float maxBoll;

  /**
   * Boll最小值
   */
  public float minBoll;
}


================================================
FILE: chartmodule/src/main/java/com/guannan/chartmodule/data/KLineToDrawItem.java
================================================
package com.guannan.chartmodule.data;

import android.graphics.RectF;
import com.guannan.simulateddata.entity.KLineItem;
import java.io.Serializable;

/**
 * @author guannan
 * @date on 2020-03-29 16:25
 * @des 绘制在当前屏幕上的每一个交易日的数据
 */
public class KLineToDrawItem implements Serializable {

  /**
   * 蜡烛线
   */
  public RectF rect;

  /**
   * 上影线、下影线
   */
  public RectF shadowRect;

  /**
   * 当天涨跌: true:跌   false:涨
   */
  public boolean isFall;

  /**
   * 当天交易日日期
   */
  public String date;

  /**
   * 成交量
   */
  public RectF volumeRect;

  /**
   * 日K数据
   */
  public KLineItem klineItem;

  public TechItem techItem;
}


================================================
FILE: chartmodule/src/main/java/com/guannan/chartmodule/data/LineRectItem.java
================================================
package com.guannan.chartmodule.data;

import android.graphics.RectF;

/**
 * @author guannan
 * @date on 2020-06-12 16:16
 */
public class LineRectItem {

  public RectF rect;

  public boolean isFall;
}


================================================
FILE: chartmodule/src/main/java/com/guannan/chartmodule/data/SubChartData.java
================================================
package com.guannan.chartmodule.data;

import android.graphics.Path;
import java.util.ArrayList;
import java.util.List;

/**
 * @author guannan
 * @date on 2020-06-12 15:48
 * @des 附图数据
 */
public class SubChartData {

  /**
   * MACD
   */
  public int MACDLINES = 2;

  /**
   * macd线
   */
  public Path[] macdPaths = new Path[2];

  public List<LineRectItem> macdRects = new ArrayList<>();

  public Path[] bollPaths = new Path[3];

  public SubChartData() {
    for (int i = 0; i < MACDLINES; i++) {
      macdPaths[i] = new Path();
    }
    for (int j = 0; j < 3; j++) {
      bollPaths[j] = new Path();
    }
  }

  public void reset() {
    if (macdPaths != null) {
      for (Path macdPath : macdPaths) {
        if (macdPath != null) {
          macdPath.reset();
        }
      }
    }
    if (bollPaths != null) {
      for (Path bollPath : bollPaths) {
        bollPath.reset();
      }
    }
    if (macdRects != null) {
      macdRects.clear();
    }
  }
}


================================================
FILE: chartmodule/src/main/java/com/guannan/chartmodule/data/TechItem.java
================================================
package com.guannan.chartmodule.data;

/**
 * @author guannan
 * @date on 2020-06-10 14:38
 * @des
 */
public class TechItem {

  // MACD(利用收盘价的短期(常用为12日)指数移动平均线与长期(常用为26日)
  // 指数移动平均线之间的聚合与分离状况,对买进、卖出时机作出研判的技术指标)
  public float dif = .0f;
  public float dea = .0f;
  public float macd = .0f;

  // BOLL
  public float boll = .0f;
  public float upper = .0f;
  public float lower = .0f;
}


================================================
FILE: chartmodule/src/main/java/com/guannan/chartmodule/helper/ChartDataSourceHelper.java
================================================
package com.guannan.chartmodule.helper;

import android.graphics.PointF;
import android.graphics.RectF;
import com.guannan.chartmodule.chart.KMasterChartView;
import com.guannan.chartmodule.chart.KSubChartView;
import com.guannan.chartmodule.data.ExtremeValue;
import com.guannan.chartmodule.data.KLineToDrawItem;
import com.guannan.chartmodule.data.LineRectItem;
import com.guannan.chartmodule.data.SubChartData;
import com.guannan.chartmodule.data.TechItem;
import com.guannan.chartmodule.inter.IChartDataCountListener;
import com.guannan.chartmodule.utils.DataUtils;
import com.guannan.chartmodule.utils.DateUtils;
import com.guannan.simulateddata.entity.KLineItem;
import java.util.ArrayList;
import java.util.List;

/**
 * @author guannan
 * @date on 2020-03-14 17:09
 * @des 行情图数据辅助类
 */
public class ChartDataSourceHelper {

  /**
   * K线相关
   */
  /**
   * 一屏默认展示的蜡烛线数量
   */
  public static int K_D_COLUMNS = 60;

  /**
   * 蜡烛线的最大值(最高价格)
   */
  public float maxPrice = Float.MIN_VALUE;

  /**
   * 蜡烛线的最小值
   */
  public float minPrice = Float.MAX_VALUE;

  /**
   * 成交量最小值
   */
  public float maxVolume = Float.MIN_VALUE;

  /**
   * MACD最大值
   */
  public float maxMacd = Float.MIN_VALUE;

  /**
   * MACD最小值
   */
  public float minMacd = Float.MAX_VALUE;

  /**
   * Boll最大值
   */
  public float maxBoll = Float.MIN_VALUE;

  /**
   * Boll最小值
   */
  public float minBoll = Float.MAX_VALUE;

  /**
   * 行情图当前屏开始的位置
   */
  public int startIndex;

  /**
   * 行情图当前屏结束位置
   */
  public int endIndex;

  /**
   * k线的绘制数据
   */
  private List<KLineToDrawItem> kLineItems;

  private List<KLineItem> mKList;

  private IChartDataCountListener<List<KLineToDrawItem>> mReadyListener;

  private KMasterChartView mKLineChartView;

  private KSubChartView mVolumeView;

  private KSubChartView mMacdView;

  /**
   * 最大值最小值放大系数
   */
  private float scale = 0.02f;

  private final TechParamsHelper mTechParamsHelper;
  private SubChartData mSubChartData;

  public enum SourceType {
    INIT,
    MOVE,
    FLING,
    SCALE
  }

  public ChartDataSourceHelper(IChartDataCountListener<List<KLineToDrawItem>> listener) {
    this.mReadyListener = listener;
    mTechParamsHelper = new TechParamsHelper();
  }

  /**
   * 初始化行情图初始数据
   */
  public void initKDrawData(List<KLineItem> klineList,
      KMasterChartView kLineChartView,
      KSubChartView volumeView, KSubChartView macdView) {

    this.mKList = klineList;
    this.mKLineChartView = kLineChartView;
    this.mVolumeView = volumeView;
    this.mMacdView = macdView;

    mSubChartData = new SubChartData();

    // K线首次当前屏初始位置
    startIndex = Math.max(0, klineList.size() - K_D_COLUMNS);
    // k线首次当前屏结束位置
    endIndex = klineList.size() - 1;
    // 计算技术指标
    mTechParamsHelper.caculateTechParams(klineList, TechParamType.BOLL);
    mTechParamsHelper.caculateTechParams(klineList, TechParamType.MACD);
    initKMoveDrawData(0, SourceType.INIT);
  }

  /**
   * 根据移动偏移量计算行情图当前屏数据
   *
   * @param distance 手指横向移动距离
   */
  public void initKMoveDrawData(float distance, SourceType sourceType) {

    // 重置默认值
    resetDefaultValue();

    // 计算当前屏幕开始和结束的位置
    countStartEndPos(distance, sourceType);

    // 计算蜡烛线价格最大最小值,成交量最大值
    ExtremeValue extremeValue = countMaxMinValue();

    // 最大值最小值差值
    float diffPrice = maxPrice - minPrice;

    // MACD最大最小值
    float diffMacd = maxMacd - minMacd;

    float diffBoll = maxBoll - minBoll;

    RectF contentRect = mKLineChartView.getViewPortHandler().mContentRect;

    // 计算当前屏幕每一个蜡烛线的位置和涨跌情况
    for (int i = startIndex, k = 0; i < endIndex; i++, k++) {
      KLineItem kLineItem = mKList.get(i);
      // 开盘价
      float open = kLineItem.open;
      // 最低价
      float close = kLineItem.close;
      // 最高价
      float high = kLineItem.high;
      // 最低价
      float low = kLineItem.low;

      KLineToDrawItem drawItem = new KLineToDrawItem();

      // 计算蜡烛线
      float scaleY_open = (maxPrice - open) / diffPrice;
      float scaleY_low = (maxPrice - close) / diffPrice;
      RectF candleRect = getRect(contentRect, k, scaleY_open, scaleY_low);
      drawItem.rect = candleRect;
      // 计算上影线,下影线
      float scale_HL_T = (maxPrice - high) / diffPrice;
      float scale_HL_B = (maxPrice - low) / diffPrice;
      RectF shadowRect = getLine(contentRect, k, scale_HL_T, scale_HL_B);
      drawItem.shadowRect = shadowRect;

      // 计算红涨绿跌,暂时这么计算(其实红涨绿跌是根据当前开盘价和前一天的收盘价做对比)
      if (i - 1 >= 0) {
        KLineItem preItem = mKList.get(i - 1);
        if (kLineItem.open > preItem.close) {
          drawItem.isFall = false;
        } else {
          drawItem.isFall = true;
        }
        if (preItem.close != 0) {
          kLineItem.preClose = preItem.close;
        } else {
          kLineItem.preClose = kLineItem.open;
        }
      }

      // 计算每一个月的第一个交易日
      if (i - 1 >= 0 && i + 1 < endIndex) {
        int currentMonth = DateUtils.getMonth(kLineItem.day);
        KLineItem preItem = mKList.get(i - 1);
        int preMonth = DateUtils.getMonth(preItem.day);
        if (currentMonth != preMonth) {
          drawItem.date = kLineItem.day.substring(0, 10);
        }
      }

      // 计算成交量
      if (Math.abs(maxVolume) > DataUtils.EPSILON) {
        RectF volumeRct = mVolumeView.getViewPortHandler().mContentRect;
        float scaleVolume = (maxVolume - kLineItem.volume) / maxVolume;
        drawItem.volumeRect = getRect(volumeRct, k, scaleVolume, 1);
      }

      // 计算BOLL
      caculateBollPath(diffBoll, contentRect, i, k, drawItem);

      // 计算附图MACD Path
      caculateMacdPath(diffMacd, i, k, drawItem.isFall);

      drawItem.klineItem = kLineItem;
      kLineItems.add(drawItem);
    }

    List<KLineToDrawItem> resultList = new ArrayList<>();
    // 数据准备完毕
    if (mReadyListener != null) {
      resultList.addAll(kLineItems);
      mReadyListener.onReady(resultList, extremeValue, mSubChartData);
    }
  }

  /**
   * 计算主图Boll指标
   */
  private void caculateBollPath(float diffBoll, RectF contentRect, int i, int k,
      KLineToDrawItem drawItem) {
    List<TechItem> listParams = mTechParamsHelper.listParams;
    TechItem techItem = listParams.get(i);
    drawItem.techItem = techItem;
    float scaleY = (maxBoll - techItem.upper) / diffBoll;
    PointF point = getPoint(contentRect, k, scaleY);
    if (i == startIndex) {
      mSubChartData.bollPaths[0].moveTo(point.x, point.y);
    } else {
      mSubChartData.bollPaths[0].lineTo(point.x, point.y);
    }

    scaleY = (maxBoll - techItem.lower) / diffBoll;
    point = getPoint(contentRect, k, scaleY);
    if (i == startIndex) {
      mSubChartData.bollPaths[1].moveTo(point.x, point.y);
    } else {
      mSubChartData.bollPaths[1].lineTo(point.x, point.y);
    }

    float midValue = (techItem.upper + techItem.lower) / 2;
    scaleY = (maxBoll - midValue) / diffBoll;
    point = getPoint(contentRect, k, scaleY);
    if (i == startIndex) {
      mSubChartData.bollPaths[2].moveTo(point.x, point.y);
    } else {
      mSubChartData.bollPaths[2].lineTo(point.x, point.y);
    }
  }

  /**
   * MACD
   */
  private void caculateMacdPath(float diffMacd, int index, int k, boolean isFall) {
    List<TechItem> listParams = mTechParamsHelper.listParams;
    TechItem techItem = listParams.get(index);
    RectF contentRect = mMacdView.getViewPortHandler().mContentRect;
    float scaleY = (maxMacd - techItem.dif) / diffMacd;
    PointF point = getPoint(contentRect, k, scaleY);
    if (index == startIndex) {
      mSubChartData.macdPaths[0].moveTo(point.x, point.y);
    } else {
      mSubChartData.macdPaths[0].lineTo(point.x, point.y);
    }

    scaleY = (maxMacd - techItem.dea) / diffMacd;
    point = getPoint(contentRect, k, scaleY);
    if (index == startIndex) {
      mSubChartData.macdPaths[1].moveTo(point.x, point.y);
    } else {
      mSubChartData.macdPaths[1].lineTo(point.x, point.y);
    }
    scaleY = techItem.macd / diffMacd;
    RectF rect = getRect(contentRect, k, scaleY);
    LineRectItem lineRectItem = new LineRectItem();
    lineRectItem.rect = rect;
    lineRectItem.isFall = techItem.macd < DataUtils.EPSILON;
    mSubChartData.macdRects.add(lineRectItem);
  }

  /**
   * 重置默认的最大值、最小值
   */
  private void resetDefaultValue() {
    // 重置最大最小值
    maxPrice = Float.MIN_VALUE;
    minPrice = Float.MAX_VALUE;
    maxVolume = Float.MIN_VALUE;
    maxMacd = Float.MIN_VALUE;
    minMacd = Float.MAX_VALUE;
    maxBoll = Float.MIN_VALUE;
    minBoll = Float.MAX_VALUE;
    if (kLineItems == null) {
      kLineItems = new ArrayList<>();
    } else {
      kLineItems.clear();
    }
    mSubChartData.reset();
  }

  /**
   * 计算当前屏幕开始和结束的位置
   */
  private void countStartEndPos(float distance,
      SourceType sourceType) {
    if (sourceType == SourceType.SCALE) {
      // TODO
      //if(endIndex){
      //
      //}
      startIndex = endIndex - K_D_COLUMNS;
    } else {
      // 根据偏移距离计算偏移几天
      int offCount = (int) ((distance * ChartDataSourceHelper.K_D_COLUMNS)
          / mKLineChartView.getViewPortHandler().mContentRect.width());
      // 计算移动后的开始和结束位置
      startIndex = startIndex - offCount;
      endIndex = startIndex + K_D_COLUMNS;
    }
    if (endIndex > mKList.size()) {
      startIndex = mKList.size() - K_D_COLUMNS;
      endIndex = startIndex + K_D_COLUMNS;
    }
    if (startIndex < 0) {
      startIndex = 0;
      endIndex = startIndex + K_D_COLUMNS;
    }
  }

  /**
   * 计算蜡烛线价格最大最小值,成交量最大值
   */
  private ExtremeValue countMaxMinValue() {
    List<TechItem> listParams = mTechParamsHelper.listParams;
    for (int i = startIndex; i < endIndex; i++) {
      KLineItem kLineItem = mKList.get(i);
      if (kLineItem != null) {
        if (kLineItem.high > maxPrice) {
          maxPrice = kLineItem.high;
        }
        if (kLineItem.low < minPrice) {
          minPrice = kLineItem.low;
        }
        if (kLineItem.volume > maxVolume) {
          maxVolume = kLineItem.volume;
        }
      }
      // 计算MACD最大值最小值
      TechItem techItem = listParams.get(i);
      TechParamsHelper.Limit limit = mTechParamsHelper.getLimitValue(techItem, TechParamType.MACD);
      if (limit.max > maxMacd) {
        maxMacd = limit.max;
      }
      if (limit.min < minMacd) {
        minMacd = limit.min;
      }

      // 计算BOLL最大最小值
      TechParamsHelper.Limit limitValue =
          mTechParamsHelper.getLimitValue(techItem, TechParamType.BOLL);
      if (limitValue.max > maxBoll) {
        maxBoll = limitValue.max;
      }
      if (limitValue.min < minBoll) {
        minBoll = limitValue.min;
      }
    }
    // 最大值、最小值缩放系数
    maxPrice = maxPrice * (1 + scale);
    maxVolume = maxVolume * (1 + scale);
    minPrice = minPrice * (1 - scale);
    maxBoll = maxBoll * (1 + scale);
    minBoll = minBoll * (1 - scale);
    maxMacd = maxMacd * (1 + scale);
    minMacd = minMacd * (1 - scale);

    ExtremeValue extremeValue = new ExtremeValue();
    extremeValue.maxPrice = maxPrice;
    extremeValue.minPrice = minPrice;
    extremeValue.maxVolume = maxVolume;
    extremeValue.macdMax = maxMacd;
    extremeValue.macdMin = minMacd;
    extremeValue.minBoll = minBoll;
    extremeValue.maxBoll = maxBoll;
    return extremeValue;
  }

  /**
   * 蜡烛图的区域,间隙0.125,蜡烛图宽度占0.75.
   */
  public RectF getRect(RectF parent, int col, float scaleTop,
      float scaleBottom) {
    RectF rect = new RectF();
    rect.left = parent.left + (int) (parent.width() * (col + 0.125)
        / K_D_COLUMNS);
    rect.right = parent.left + (int) (parent.width() * (col + 0.875)
        / K_D_COLUMNS);
    if (rect.right == rect.left) {
      rect.right++;
    }
    rect.top = parent.top + (int) (parent.height() * scaleTop);
    rect.bottom = parent.top + (int) (parent.height() * scaleBottom);
    if (rect.top == rect.bottom) {
      rect.bottom += 1;
    }
    return rect;
  }

  /**
   * 蜡烛线上影线,和下影线的宽度和高度
   */
  public RectF getLine(RectF parent, int col, float scaleTop,
      float scaleBottom) {
    RectF rect = new RectF();
    rect.left = (int) (parent.left + (int) (parent.width() * (col + 0.5)
        / K_D_COLUMNS) - 1.5);
    rect.right = (int) (parent.left + (int) (parent.width() * (col + 0.5)
        / K_D_COLUMNS) + 1.5);

    rect.top = parent.top + (int) (parent.height() * scaleTop);
    rect.bottom = parent.top + (int) (parent.height() * scaleBottom);
    return rect;
  }

  public PointF getPoint(RectF rect, int col, float scaleY) {
    PointF point = new PointF();
    point.x = rect.left + (int) (rect.width() * (col + 0.5) / K_D_COLUMNS);
    point.y = rect.top + (int) (rect.height() * scaleY);
    return point;
  }

  public RectF getRect(RectF techRect, int col, float scaleY) {
    RectF rect = new RectF();
    rect.left = techRect.left + (int) (techRect.width() * (col + 0.4)
        / K_D_COLUMNS);
    rect.right = techRect.left + (int) (techRect.width() * (col + 0.6)
        / K_D_COLUMNS);
    if (rect.right == rect.left) {
      rect.right++;
    }
    float mid = rect.centerX();
    if (rect.left + 4 < rect.right) {
      rect.left = mid - 2;
      rect.right = mid + 2;
    }
    mid = techRect.centerY();
    float otherY = mid - techRect.height() * scaleY;
    if (mid > otherY) {
      rect.top = otherY;
      rect.bottom = mid;
    } else {
      rect.top = mid;
      rect.bottom = otherY;
    }
    if (rect.top == rect.bottom) {
      rect.bottom += 1;
    }
    return rect;
  }
}


================================================
FILE: chartmodule/src/main/java/com/guannan/chartmodule/helper/ChartTouchHelper.java
================================================
package com.guannan.chartmodule.helper;

import android.content.Context;
import android.graphics.PointF;
import android.hardware.SensorManager;
import android.os.Handler;
import android.os.Looper;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import androidx.core.view.GestureDetectorCompat;
import com.guannan.chartmodule.inter.IChartGestureListener;
import com.guannan.chartmodule.utils.DataUtils;
import com.guannan.chartmodule.utils.DisplayUtils;

/**
 * @author guannan
 * @date on 2020-03-07 16:00
 * @des 图表的手势操作相关
 */
public class ChartTouchHelper
    extends GestureDetector.SimpleOnGestureListener implements View.OnTouchListener {

  /**
   * 两个手指之间的距离
   */
  private float mSavedDist = 1f;

  /**
   * 两个手指之间在X轴的距离
   */
  private float mSavedXDist = 1f;

  /**
   * 手指fling的最小速度 默认:50px/s
   */
  private int mMinFlingVelocity;

  /**
   * 手指fling的最大速度  默认:8000px/s
   */
  private int mMaxFlingVelocity;

  /**
   * 加速度
   */
  private float mDeceleration;
  private final Handler mHandler;

  /**
   * 计算fling滑动的距离
   */
  private FlingRunnable mFlingRunnable;

  public enum ChartGesture {
    NONE, DRAG, X_ZOOM, SINGLE_TAP, DOUBLE_TAP, LONG_PRESS, FLING
  }

  protected static final int NONE = 0;

  /**
   * 拖动
   */
  protected static final int DRAG = 1;

  /**
   * x轴方向缩放
   */
  protected static final int X_ZOOM = 2;

  /**
   * 长按
   */
  protected static final int LONG_PRESS = 3;

  /**
   * 默认的手势动作
   */
  private ChartGesture mLastGesture = ChartGesture.NONE;

  /**
   * 当前的手势状态
   */
  private int mTouchMode = NONE;

  /**
   * 手势识别构造器
   */
  private final GestureDetectorCompat mDetectorCompat;

  /**
   * 手势监听回调
   */
  private IChartGestureListener mChartGestureListener;

  /**
   * 多指触摸的时候,两个手指之间的中点
   */
  private PointF mTouchPointCenter = new PointF();

  /**
   * 当前手指触摸的开始位置
   */
  private PointF mTouchStartPoint = new PointF();

  /**
   * 缩放距离达到3.5dp认为是缩放
   */
  private float mMinScalePointerDistance;

  /**
   * x轴移动距离,大于18像素认为是移动
   */
  private float mXMoveDist = 18f;

  /**
   * X轴滚动距离
   */
  private float scrollX;

  public ChartTouchHelper(View view) {
    Context context = view.getContext();
    mDetectorCompat = new GestureDetectorCompat(context, this);
    view.setOnTouchListener(this);
    initVelocity(context);
    mHandler = new Handler(Looper.getMainLooper());
  }

  private void initVelocity(Context context) {
    mMinScalePointerDistance = DisplayUtils.dip2px(context, 3.5f);
    ViewConfiguration configuration = ViewConfiguration.get(context);
    mMinFlingVelocity = configuration.getScaledMinimumFlingVelocity();
    mMaxFlingVelocity = configuration.getScaledMaximumFlingVelocity();

    // 加速度
    float ppi = context.getResources().getDisplayMetrics().density * 160.0f;
    mDeceleration = SensorManager.GRAVITY_EARTH
        * 39.37f // inch/meter
        * ppi // pixels per inch
        * ViewConfiguration.getScrollFriction() * 4;
  }

  /**
   * 设置手势监听回调
   */
  public void setChartGestureListener(IChartGestureListener listener) {
    this.mChartGestureListener = listener;
  }

  /**
   * 手势触摸开始
   */
  public void startAction(MotionEvent event) {
    if (mChartGestureListener != null) {
      mChartGestureListener.onChartGestureStart(event, mLastGesture);
    }
  }

  /**
   * 手势触摸结束
   */
  public void endAction(MotionEvent event) {
    if (mChartGestureListener != null) {
      mChartGestureListener.onChartGestureEnd(event, mLastGesture);
    }
  }

  @Override
  public boolean onTouch(View v, MotionEvent event) {

    mDetectorCompat.onTouchEvent(event);
    // 同时处理单指和多指的手势动作
    switch (event.getAction() & MotionEvent.ACTION_MASK) {
      case MotionEvent.ACTION_DOWN:
        // 记录手势的开始
        startAction(event);
        // 手势触摸的开始位置
        saveTouchStart(event);
        stopFling();
        break;
      case MotionEvent.ACTION_POINTER_DOWN:
        if (event.getPointerCount() >= 2) {
          saveTouchStart(event);
          // 两个手指之间在X轴的距离
          mSavedXDist = getXDist(event);
          // 两个手指之间的距离
          mSavedDist = spacing(event);
          // 两个手指之间距离大于10才认为是缩放
          if (mSavedDist > 10f) {
            mTouchMode = X_ZOOM;
          }
          // 计算两个手指之间的中点位置
          midPoint(mTouchPointCenter, event);
        }
        break;
      case MotionEvent.ACTION_MOVE:
        if (mTouchMode == DRAG) {
          mLastGesture = ChartGesture.DRAG;
        } else if (mTouchMode == X_ZOOM) {
          if (event.getPointerCount() >= 2) {

            // 手指移动的距离
            float totalDist = spacing(event);

            if (totalDist > mMinScalePointerDistance) {
              if (mTouchMode == X_ZOOM) {
                mLastGesture = ChartGesture.X_ZOOM;
                float xDist = getXDist(event);
                float scaleX = xDist / mSavedXDist;
                if (mChartGestureListener != null) {
                  mChartGestureListener.onChartScale(event, scaleX, 1);
                }
              }
            }
          }
        } else if (mTouchMode == LONG_PRESS) {
          mLastGesture = ChartGesture.LONG_PRESS;
          if (mChartGestureListener != null) {
            mChartGestureListener.onChartLongPressed(event);
          }
        } else if (mTouchMode == NONE) {
          // 移动之后重置起始位置
          saveTouchStart(event);
          mLastGesture = ChartGesture.DRAG;
          mTouchMode = DRAG;
        }

        break;
      case MotionEvent.ACTION_UP:
      case MotionEvent.ACTION_POINTER_UP:
      case MotionEvent.ACTION_CANCEL:
        mTouchMode = NONE;
        endAction(event);
        break;
    }

    return true;
  }

  @Override
  public boolean onDoubleTap(MotionEvent e) {
    mLastGesture = ChartGesture.DOUBLE_TAP;
    if (mChartGestureListener != null) {
      mChartGestureListener.onChartDoubleTapped(e);
    }
    return super.onDoubleTap(e);
  }

  @Override
  public void onLongPress(MotionEvent e) {
    mTouchMode = LONG_PRESS;
    if (mChartGestureListener != null) {
      mChartGestureListener.onChartLongPressed(e);
    }
  }

  @Override
  public boolean onSingleTapUp(MotionEvent e) {
    mLastGesture = ChartGesture.SINGLE_TAP;
    if (mChartGestureListener != null) {
      mChartGestureListener.onChartSingleTapped(e);
    }
    return super.onSingleTapUp(e);
  }

  /**
   * @param e1 手指按下的位置
   * @param e2 手指抬起的位置
   * @param velocityX 手指抬起时的x轴的加速度  px/s
   */
  @Override
  public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
    mLastGesture = ChartGesture.FLING;
    fling(velocityX, e2.getX() - e1.getX());
    return true;
  }

  private void fling(float velocity, float offset) {
    stopFling();
    if (Math.abs(mDeceleration) > DataUtils.EPSILON) {
      // 根据加速度计算速度减少到0时的时间
      int duration = (int) (1000 * velocity / mDeceleration);
      // 手指抬起时,缓冲的距离
      int totalDistance = (int) ((velocity * velocity) / (mDeceleration + mDeceleration));
      int startX = (int) offset, flingX;
      if (velocity < 0) {
        flingX = startX - totalDistance;
      } else {
        flingX = startX + totalDistance;
      }
      mFlingRunnable = new FlingRunnable(startX, flingX, duration, mHandler, mChartGestureListener);
      mHandler.post(mFlingRunnable);
    }
  }

  private void stopFling() {
    if (mFlingRunnable != null) {
      mFlingRunnable.stop();
      mHandler.removeCallbacks(mFlingRunnable);
    }
  }

  /**
   * @param e1 down的时候event
   * @param e2 move的时候event
   * @param distanceX x轴移动距离:两个move之间差值
   * @param distanceY y轴移动距离
   */
  @Override
  public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {

    if (mChartGestureListener != null) {
      scrollX -= distanceX;
      // 当X轴移动距离大于18px认为是移动
      if (Math.abs(scrollX) > mXMoveDist) {
        mChartGestureListener.onChartTranslate(e2, scrollX);
        scrollX = 0;
      }
    }
    if (Math.abs(distanceX) > Math.abs(distanceY)) {
      return true;
    } else {
      return false;
    }
  }

  /**
   * 计算两个手指在X轴方向的距离
   */
  private static float getXDist(MotionEvent e) {
    float x = Math.abs(e.getX(0) - e.getX(1));
    return x;
  }

  /**
   * 计算两个手指之间真正的距离
   */
  private static float spacing(MotionEvent event) {
    float x = event.getX(0) - event.getX(1);
    float y = event.getY(0) - event.getY(1);
    return (float) Math.sqrt(x * x + y * y);
  }

  /**
   * 计算两个手指触摸的中点
   */
  private static void midPoint(PointF point, MotionEvent event) {
    float x = event.getX(0) + event.getX(1);
    float y = event.getY(0) + event.getY(1);
    point.x = (x / 2f);
    point.y = (y / 2f);
  }

  /**
   * 当前手机触摸的开始位置
   */
  private void saveTouchStart(MotionEvent event) {
    mTouchStartPoint.x = event.getX();
    mTouchStartPoint.y = event.getY();
  }

  /**
   * 返回两个点之间的距离
   */
  protected static float distance(float eventX, float startX, float eventY, float startY) {
    float dx = eventX - startX;
    float dy = eventY - startY;
    return (float) Math.sqrt(dx * dx + dy * dy);
  }

  private static final class FlingRunnable implements Runnable {
    // 一秒40帧
    private final static int MAX_FPS = 40;
    protected static final int ANIMATION_FPS = 1000 / MAX_FPS;
    private final int fromX;
    private final int toX;
    private final int duration;
    private Handler mHandler;
    private final Interpolator interpolator;
    private IChartGestureListener mGestureListener;
    private boolean continueRunning = true;
    private long startTime = -1;
    private float flingX;

    public FlingRunnable(int startX, int toX, int duration, Handler handler,
        IChartGestureListener gestureListener) {
      this.fromX = startX;
      this.toX = toX;
      this.duration = Math.abs(duration);
      this.interpolator = new DecelerateInterpolator();
      this.mHandler = handler;
      this.mGestureListener = gestureListener;
    }

    @Override
    public void run() {

      long remainTime = 0L;
      if (this.startTime == -1) {
        this.startTime = System.currentTimeMillis();
      } else if (duration > 0) {
        remainTime = (1000 * (System.currentTimeMillis() - startTime))
            / duration;
        remainTime = Math.max(Math.min(remainTime, 1000), 0);
      }
      // 每时间差内移动的距离
      final int deltaX = Math
          .round((fromX - toX)
              * interpolator
              .getInterpolation(remainTime / 1000f));
      int remainDistance = fromX - deltaX;

      if (continueRunning && toX != remainDistance) {
        flingX -= deltaX;
        if (mGestureListener != null && Math.abs(flingX) > 18) {
          mGestureListener.onChartFling(flingX);
          flingX = deltaX;
        }
        mHandler.postDelayed(this, ANIMATION_FPS);
      }
    }

    public void stop() {
      this.continueRunning = false;
      mHandler.removeCallbacks(this);
      if (mGestureListener != null) {
        mGestureListener.onChartFling(0);
      }
    }
  }
}


================================================
FILE: chartmodule/src/main/java/com/guannan/chartmodule/helper/TechParamType.java
================================================
package com.guannan.chartmodule.helper;

/**
 * @author guannan
 * @date on 2020-06-12 12:12
 * @des 技术指标类型
 */
public enum TechParamType {
  VOLUME,
  MACD,
  BOLL
}


================================================
FILE: chartmodule/src/main/java/com/guannan/chartmodule/helper/TechParamsHelper.java
================================================
package com.guannan.chartmodule.helper;

import com.guannan.chartmodule.data.TechItem;
import com.guannan.chartmodule.utils.DataUtils;
import com.guannan.simulateddata.entity.KLineItem;
import java.util.ArrayList;
import java.util.List;

/**
 * @author guannan
 * @date on 2020-06-10 16:12
 * @des 技术指标相关计算
 */
public class TechParamsHelper {

  public class Limit {
    public float max = .0f;
    public float min = .0f;
  }

  public List<TechItem> listParams = new ArrayList<>();

  private TechItem getTechItem(int index) {
    int size = listParams.size();
    TechItem itemT = null;
    if (index < size) {
      itemT = listParams.get(index);
    } else {
      while (index >= size) {
        itemT = new TechItem();
        listParams.add(itemT);
        size++;
      }
    }
    return itemT;
  }

  /**
   * 获取技术指标的极值
   */
  public Limit getLimitValue(TechItem techItem, TechParamType techParamType) {
    Limit limit = new Limit();
    if (techParamType == TechParamType.MACD) {
      limit.max = techItem.dea;
      limit.min = techItem.dif;
      if (techItem.dea < techItem.dif) {
        limit.max = techItem.dif;
        limit.min = techItem.dea;
      }
      if (limit.max < techItem.macd) {
        limit.max = techItem.macd;
      }
      if (limit.min > techItem.macd) {
        limit.min = techItem.macd;
      }
    } else if (techParamType == TechParamType.BOLL) {
      limit.max = techItem.upper;
      limit.min = techItem.lower;
    }
    return limit;
  }

  /**
   * 计算技术指标
   */
  public void caculateTechParams(List<KLineItem> list, TechParamType techParamType) {
    // 附图MACD
    if (techParamType == TechParamType.MACD) {
      linkDataMACD(list);
    } else if (techParamType == TechParamType.BOLL) {
      linkDataBOLL(list);
    }
  }

  /**
   * BOLL
   */
  private void linkDataBOLL(List<KLineItem> list) {
    int len = list.size();
    KLineItem itemK;
    TechItem itemT;

    final int _EN = 20;
    float sum = .0f, std2 = .0f, stdsum = .0f;
    float tmpArr[] = new float[len];

    for (int i = 0; i < len; i++) {
      itemK = list.get(i);
      itemT = getTechItem(i);

      sum += itemK.close;
      if (i >= _EN - 1) {
        itemT.boll = sum / _EN;
        sum -= list.get(i - _EN + 1).close;
      } else {
        itemT.boll = sum / (i + 1);
      }
      // 差值的平方
      tmpArr[i] = (float) Math.pow((itemK.close - itemT.boll), 2);
      stdsum += tmpArr[i];
      if (i > _EN - 1) {
        stdsum -= tmpArr[i - _EN];
        if (stdsum < .0) {
          stdsum = Math.abs(stdsum);
        }
      }
      std2 = (float) (2 * Math.sqrt(stdsum / Math.min(i + 1, _EN)));
      if (std2 < DataUtils.EPSILON) {
        std2 = itemT.boll * 0.1f;
      }
      itemT.upper = itemT.boll + std2;
      itemT.lower = itemT.boll - std2;
    }
  }

  /**
   * MACD
   */
  public void linkDataMACD(List<KLineItem> list) {
    int len = list.size();
    KLineItem itemK = list.get(0);
    TechItem techItem = null;
    TechItem preTechItem = getTechItem(0);

    final int _E1 = 12, _E2 = 26, _EDEA = 9;
    float ema1 = itemK.close, ema2 = itemK.close;

    for (int i = 1; i < len; i++) {
      itemK = list.get(i);
      techItem = getTechItem(i);

      ema1 = _calcEMA(ema1, itemK.close, _E1);
      ema2 = _calcEMA(ema2, itemK.close, _E2);
      techItem.dif = ema1 - ema2;
      techItem.dea = _calcEMA(preTechItem.dea, techItem.dif, _EDEA);
      techItem.macd = (techItem.dif - techItem.dea) * 2;

      preTechItem = techItem;
    }
  }

  public static float _calcEMA(float ema0_, float close_, int cycle_) {
    int cycleDiv = cycle_ + 1;
    return (cycle_ - 1) * ema0_ / cycleDiv + close_ * 2 / cycleDiv;
  }
}


================================================
FILE: chartmodule/src/main/java/com/guannan/chartmodule/inter/IChartDataCountListener.java
================================================
package com.guannan.chartmodule.inter;

import com.guannan.chartmodule.data.ExtremeValue;
import com.guannan.chartmodule.data.SubChartData;

/**
 * @author guannan
 * @date on 2020-04-11 13:57
 * @des 行情图数据准备完毕回调
 */
public interface IChartDataCountListener<T> {

  void onReady(T data, ExtremeValue extremeValue, SubChartData subChartData);
}


================================================
FILE: chartmodule/src/main/java/com/guannan/chartmodule/inter/IChartGestureListener.java
================================================
package com.guannan.chartmodule.inter;

import android.view.MotionEvent;
import com.guannan.chartmodule.helper.ChartTouchHelper;

/**
 * @author guannan
 * @date on 2020-03-14 11:28
 * @des 手势识别监听
 */
public interface IChartGestureListener {

  /**
   * 当手势开始的时候触发,一般是(ACTION_DOWN)
   *
   * @param me 事件
   * @param lastPerformedGesture 最后的手势类型
   */
  void onChartGestureStart(MotionEvent me, ChartTouchHelper.ChartGesture lastPerformedGesture);

  /**
   * 当手势结束的时候,一般是(ACTION_UP, ACTION_CANCEL)
   *
   * @param me 事件
   * @param lastPerformedGesture 最后的手势类型
   */
  void onChartGestureEnd(MotionEvent me, ChartTouchHelper.ChartGesture lastPerformedGesture);

  /**
   * 当在图表上执行长按的时候调用
   */
  void onChartLongPressed(MotionEvent me);

  /**
   * 当在图表上执行双击的时候调用
   */
  void onChartDoubleTapped(MotionEvent me);

  /**
   * 当在图表上执行单击的时候调用
   */
  void onChartSingleTapped(MotionEvent me);

  /**
   * 当在图表上执行fling的时候调用
   */
  void onChartFling(float distanceX);

  /**
   * 当在图表上执行缩放的时候调用
   *
   * @param scaleX x轴缩放系数
   * @param scaleY y轴缩放系数
   */
  void onChartScale(MotionEvent me, float scaleX, float scaleY);

  /**
   * 当在图标执行move,drag的时候调用
   *
   * @param dX x轴移动距离
   */
  void onChartTranslate(MotionEvent me, float dX);
}


================================================
FILE: chartmodule/src/main/java/com/guannan/chartmodule/inter/IPressChangeListener.java
================================================
package com.guannan.chartmodule.inter;

import android.view.MotionEvent;

/**
 * @author guannan
 * @date on 2020-04-21 20:09
 * @des TODO
 */
public interface IPressChangeListener {

  void onChartTranslate(MotionEvent me, float dX);

  void onChartFling(float distanceX);

  void onChartScale(MotionEvent me, float scaleX, float scaleY);
}


================================================
FILE: chartmodule/src/main/java/com/guannan/chartmodule/inter/ITouchResponseListener.java
================================================
package com.guannan.chartmodule.inter;

/**
 * @author guannan
 * @date on 2020-04-21 20:01
 * @des 各种手势
 */
public interface ITouchResponseListener {

}


================================================
FILE: chartmodule/src/main/java/com/guannan/chartmodule/utils/DataUtils.java
================================================
package com.guannan.chartmodule.utils;

/**
 * @author guannan
 * @date on 2020-04-08 20:02
 * @des 常量
 */
public class DataUtils {

  /**
   * +0
   */
  public static float EPSILON = 0.0000001f;

  /**
   * -0
   */
  public static float EPSILONNGT = -0.0000001f;

  /**
   * 浮点零值判定
   */
  public static boolean isZero(float val) {
    return (val > EPSILONNGT && val < EPSILON);
  }
}


================================================
FILE: chartmodule/src/main/java/com/guannan/chartmodule/utils/DateUtils.java
================================================
package com.guannan.chartmodule.utils;

/**
 * @author guannan
 * @date on 2020-04-11 14:11
 * @des 日期相关处理
 */
public class DateUtils {

  public static int getMonth(String date) {
    if (date != null && date.length() >= 10) {
      // "yyyy-MM-dd"
      StringBuffer sb = new StringBuffer(date);
      int month = Short.parseShort(sb.substring(5, 7));
      return month;
    }
    return -1;
  }

  public static String getYMD(String date) {
    if (date != null && date.length() >= 10) {
      return date.substring(0, 10);
    }
    return date;
  }
}


================================================
FILE: chartmodule/src/main/java/com/guannan/chartmodule/utils/DisplayUtils.java
================================================
package com.guannan.chartmodule.utils;

import android.app.Activity;
import android.content.Context;
import android.util.DisplayMetrics;

/**
 * @author guannan
 * @date on 2020-03-14 17:24
 * @des 屏幕尺寸相关工具类
 */
public class DisplayUtils {

  private static DisplayMetrics dMetrics = null;
  private static int DisplayMaxWidth = 0;
  private static int DisplayWidth = 0;

  /**
   * 根据手机的分辨率从 dp 的单位 转成为 px(像素)
   */
  public static int dip2px(Context context, float dpValue) {
    if (context == null) return 0;
    final float scale = context.getResources().getDisplayMetrics().density;
    return (int) (dpValue * scale + 0.5f);
  }

  /**
   * 根据手机的分辨率从 px(像素) 的单位 转成为 dp
   */
  public static int px2dip(Context context, float pxValue) {
    if (context == null) return 0;
    final float scale = context.getResources().getDisplayMetrics().density;
    return (int) (pxValue / scale + 0.5f);
  }

  public static DisplayMetrics getDisplayMetrics(Context activity) {
    if (dMetrics == null && activity != null) {
      dMetrics = activity.getApplicationContext().getResources().getDisplayMetrics();
    }
    return dMetrics;
  }

  /**
   * 获取屏幕宽度
   */
  public static int getDisPlayMaxWidth(Activity activity) {
    if (DisplayMaxWidth <= 0) {
      DisplayMetrics dMetrics = getDisplayMetrics(activity);
      if (dMetrics != null) {
        if (dMetrics.widthPixels < dMetrics.heightPixels) {
          DisplayMaxWidth = dMetrics.heightPixels;
        } else {
          DisplayMaxWidth = dMetrics.widthPixels;
        }
      }
    }

    if (DisplayMaxWidth <= 0) {
      return 800;
    } else {
      return DisplayMaxWidth;
    }
  }

  public static int getDisPlayWidth(Activity activity) {
    if (DisplayWidth <= 0) {
      DisplayMetrics dMetrics = getDisplayMetrics(activity);
      if (dMetrics != null) {
        if (dMetrics.widthPixels < dMetrics.heightPixels) {
          DisplayWidth = dMetrics.widthPixels;
        } else {
          DisplayWidth = dMetrics.heightPixels;
        }
      }
    }

    if (DisplayWidth <= 0) {
      return 480;
    } else {
      return DisplayWidth;
    }
  }
}


================================================
FILE: chartmodule/src/main/java/com/guannan/chartmodule/utils/LogUtils.java
================================================
package com.guannan.chartmodule.utils;

import android.util.Log;

/**
 * @author guannan
 * @date on 2020-03-14 10:43
 * @des 日志工具类
 */
public class LogUtils {

  private static final String TAG = "guannan_log";

  public static void d(String msg) {
    Log.d(TAG, "d: " + msg);
  }
}


================================================
FILE: chartmodule/src/main/java/com/guannan/chartmodule/utils/NumFormatUtils.java
================================================
package com.guannan.chartmodule.utils;

import java.math.BigDecimal;

/**
 * @author guannan
 * @date on 2020-04-11 16:09
 * @des 格式化数字
 */
public class NumFormatUtils {

  public static float formatFloat(float d, int digits) {
    try {
      BigDecimal b = new BigDecimal(d);
      b = b.setScale(digits, BigDecimal.ROUND_HALF_UP);
      return b.floatValue();
    } catch (Exception e) {

    }
    return d;
  }

  /**
   * 支持负数的格式化(带单位)
   */
  public static String formatBigFloatAll(float d, int digits) {
    if (d < DataUtils.EPSILONNGT) {
      if (d > -1E4) {
        return formatFloat(d, 0) + "";
      } else if (d > -1E8) {
        return formatFloat((float) (d / 1E4), digits) + "万";
      } else {
        return formatFloat((float) (d / 1E8), digits) + "亿";
      }
    } else {
      if (d < 1E4) {
        return formatFloat(d, 0) + "";
      } else if (d < 1E8) {
        return formatFloat((float) (d / 1E4), digits) + "万";
      } else if (d < 1E12) {
        return formatFloat((float) (d / 1E8), digits) + "亿";
      } else {
        if (d < 1E13) {
          return formatFloat((float) (d / 1E8), digits) + "亿";
        } else {
          return formatFloat((float) (d / 1E12), digits) + "万亿";
        }
      }
    }
  }

  public static String formatFloat(float d, int digits, boolean isAddPercentSign,
      boolean isAddPlus, String defaultRet, String unit, boolean isZeroReturnDefault) {

    if (isZeroReturnDefault && DataUtils.isZero(d)) {
      return defaultRet;
    }
    try {
      BigDecimal b = new BigDecimal(Float.toString(d));
      StringBuilder sb = new StringBuilder(b.setScale(digits, BigDecimal.ROUND_HALF_UP).toString());
      if (isAddPercentSign) {
        sb.append('%');
      } else {
        sb.append(unit);
      }

      if (d > 0 && isAddPlus) {
        sb.insert(0, '+');
      }
      return sb.toString();
    } catch (NumberFormatException e) {
      return defaultRet;
    }
  }
}


================================================
FILE: chartmodule/src/main/java/com/guannan/chartmodule/utils/PaintUtils.java
================================================
package com.guannan.chartmodule.utils;

import android.content.Context;
import android.graphics.Paint;
import android.util.TypedValue;

/**
 * @author guannan
 * @date on 2020-04-11 14:18
 * @des 画笔工具类
 */
public class PaintUtils {

  private static int TEXT_SIZE_SP;

  /**
   * 行情图外部分隔线
   */
  public static Paint GRID_DIVIDER = new Paint();
  public final static int COLOR_GRID_DIVIDER = 0xffe5e6f2;

  /**
   * 行情图内部分隔线
   */
  public static Paint GRID_INNER_DIVIDER = new Paint();

  /**
   * 表格外文字
   */
  public static Paint TEXT_PAINT = new Paint();
  public final static int C_FRAME_FONT = 0xff747985;

  /**
   * 选中十字线
   */
  public static Paint FOCUS_LINE_PAINT = new Paint();
  public final static int C_FOCUS_BG_BLACK = 0xff000000;

  /**
   * 长按弹框背景颜色
   */
  public static Paint POP_DIALOG_PAINT = new Paint();
  public final static int C_POP_DIALOG_BG = 0x99091622;

  /**
   * 表格外文字
   */
  public static Paint TEXT_POP_PAINT = new Paint();
  public final static int C_TEXT_POP = 0xffffffff;

  /**
   * 红
   */
  public static Paint TEXT_RED_PAINT = new Paint();
  public final static int C_TEXT_RED = 0xfffd4331;

  /**
   * 绿
   */
  public static Paint TEXT_GREEN_PAINT = new Paint();
  public final static int C_TEXT_GREEN = 0xff05aa3b;

  /**
   * 黄
   */
  public static Paint LINE_YELLOW_PAINT = new Paint();
  public static Paint TEXT_YELLOW_PAINT = new Paint();
  public final static int C_LINE_DEA = 0XFFFFA100;

  /**
   * 蓝
   */
  public static Paint LINE_BLUE_PAINT = new Paint();
  public static Paint TEXT_BLUE_PAINT = new Paint();
  public final static int C_LINE_DIF = 0XFF4E8BEE;

  /**
   * 紫
   */
  public static Paint LINE_PURPLE_PAINT = new Paint();
  public static Paint TEXT_PURPLE_PAINT = new Paint();
  public final static int C_LINE_MACD = 0XFFFF3FBE;

  static {

    GRID_DIVIDER.setColor(COLOR_GRID_DIVIDER);
    GRID_DIVIDER.setStyle(Paint.Style.STROKE);
    GRID_DIVIDER.setStrokeWidth(2);
    GRID_DIVIDER.setAntiAlias(true);

    GRID_INNER_DIVIDER.setColor(COLOR_GRID_DIVIDER);
    GRID_INNER_DIVIDER.setStyle(Paint.Style.STROKE);
    GRID_INNER_DIVIDER.setStrokeWidth(1);
    GRID_INNER_DIVIDER.setAntiAlias(true);

    TEXT_PAINT.setAntiAlias(true);
    TEXT_PAINT.setColor(C_FRAME_FONT);

    FOCUS_LINE_PAINT.setColor(C_FOCUS_BG_BLACK);
    FOCUS_LINE_PAINT.setStrokeWidth(2);

    POP_DIALOG_PAINT.setColor(C_POP_DIALOG_BG);
    POP_DIALOG_PAINT.setAntiAlias(true);
    POP_DIALOG_PAINT.setStyle(Paint.Style.FILL);

    TEXT_POP_PAINT.setColor(C_TEXT_POP);
    TEXT_POP_PAINT.setAntiAlias(true);

    TEXT_RED_PAINT.setColor(C_TEXT_RED);
    TEXT_RED_PAINT.setAntiAlias(true);

    TEXT_GREEN_PAINT.setColor(C_TEXT_GREEN);
    TEXT_GREEN_PAINT.setAntiAlias(true);

    LINE_YELLOW_PAINT.setStyle(Paint.Style.STROKE);
    LINE_YELLOW_PAINT.setStrokeWidth(2);
    LINE_YELLOW_PAINT.setColor(C_LINE_DEA);
    LINE_YELLOW_PAINT.setStrokeCap(Paint.Cap.ROUND);
    LINE_YELLOW_PAINT.setAntiAlias(true);

    TEXT_YELLOW_PAINT.setStyle(Paint.Style.FILL);
    TEXT_YELLOW_PAINT.setColor(C_LINE_DEA);
    TEXT_YELLOW_PAINT.setAntiAlias(true);

    LINE_BLUE_PAINT.setStyle(Paint.Style.STROKE);
    LINE_BLUE_PAINT.setStrokeWidth(2);
    LINE_BLUE_PAINT.setColor(C_LINE_DIF);
    LINE_BLUE_PAINT.setStrokeCap(Paint.Cap.ROUND);
    LINE_BLUE_PAINT.setAntiAlias(true);

    TEXT_BLUE_PAINT.setStyle(Paint.Style.FILL);
    TEXT_BLUE_PAINT.setColor(C_LINE_DIF);
    TEXT_BLUE_PAINT.setAntiAlias(true);

    LINE_PURPLE_PAINT.setStyle(Paint.Style.STROKE);
    LINE_PURPLE_PAINT.setStrokeWidth(2);
    LINE_PURPLE_PAINT.setColor(C_LINE_MACD);
    LINE_PURPLE_PAINT.setStrokeCap(Paint.Cap.ROUND);
    LINE_PURPLE_PAINT.setAntiAlias(true);

    TEXT_PURPLE_PAINT.setStyle(Paint.Style.FILL);
    TEXT_PURPLE_PAINT.setColor(C_LINE_MACD);
    TEXT_PURPLE_PAINT.setAntiAlias(true);
  }

  public static void init(Context context) {
    TEXT_SIZE_SP = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 8,
        context.getResources().getDisplayMetrics());
    TEXT_PAINT.setTextSize(TEXT_SIZE_SP);
    TEXT_YELLOW_PAINT.setTextSize(TEXT_SIZE_SP);
    TEXT_BLUE_PAINT.setTextSize(TEXT_SIZE_SP);
    TEXT_PURPLE_PAINT.setTextSize(TEXT_SIZE_SP);
    float textSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 12,
        context.getResources().getDisplayMetrics());
    TEXT_POP_PAINT.setTextSize(textSize);
    TEXT_RED_PAINT.setTextSize(textSize);
    TEXT_GREEN_PAINT.setTextSize(textSize);
  }
}


================================================
FILE: chartmodule/src/main/java/com/guannan/chartmodule/utils/StringUtils.java
================================================
package com.guannan.chartmodule.utils;

/**
 * @author guannan
 * @date on 2020-06-10 19:54
 * @des 字符串操作相关
 */
public class StringUtils {

  /**
   * 获取拼接的参数 key:value 最高价:123.00
   */
  public static String getAppendStr(String arg1, float arg2) {
    StringBuilder stringBuilder = new StringBuilder();
    stringBuilder.append(arg1);
    stringBuilder.append(arg2);
    return stringBuilder.toString();
  }

  /**
   * 获取拼接的参数 key:value 最高价:123.00
   */
  public static String getAppendStr(String arg1, String arg2) {
    StringBuilder stringBuilder = new StringBuilder();
    stringBuilder.append(arg1);
    stringBuilder.append(arg2);
    return stringBuilder.toString();
  }
}


================================================
FILE: chartmodule/src/main/res/values/colors.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>

  <!--红涨绿跌:红-->
  <color name="color_fd4331">#fd4331</color>
  <!--红涨绿跌:绿-->
  <color name="color_05aa3b">#05aa3b</color>

  <color name="color_e5e6f2">#e5e6f2</color>

</resources>


================================================
FILE: chartmodule/src/main/res/values/strings.xml
================================================
<resources>
  <string name="app_name">chartModule</string>
  <string name="date">日期:</string>
  <string name="open">开:</string>
  <string name="hign">高:</string>
  <string name="low">低:</string>
  <string name="close">收:</string>
  <string name="diff">涨跌额:</string>
  <string name="chg">涨跌幅:</string>
</resources>


================================================
FILE: gradle/wrapper/gradle-wrapper.properties
================================================
#Fri Feb 21 13:20:34 CST 2020
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip


================================================
FILE: gradle.properties
================================================
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
# AndroidX package structure to make it clearer which packages are bundled with the
# Android operating system, and which are packaged with your app's APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true
# Automatically convert third-party libraries to use AndroidX
android.enableJetifier=true



================================================
FILE: gradlew
================================================
#!/usr/bin/env sh

##############################################################################
##
##  Gradle start up script for UN*X
##
##############################################################################

# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
    ls=`ls -ld "$PRG"`
    link=`expr "$ls" : '.*-> \(.*\)$'`
    if expr "$link" : '/.*' > /dev/null; then
        PRG="$link"
    else
        PRG=`dirname "$PRG"`"/$link"
    fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null

APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`

# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""

# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"

warn () {
    echo "$*"
}

die () {
    echo
    echo "$*"
    echo
    exit 1
}

# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
  CYGWIN* )
    cygwin=true
    ;;
  Darwin* )
    darwin=true
    ;;
  MINGW* )
    msys=true
    ;;
  NONSTOP* )
    nonstop=true
    ;;
esac

CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar

# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
        # IBM's JDK on AIX uses strange locations for the executables
        JAVACMD="$JAVA_HOME/jre/sh/java"
    else
        JAVACMD="$JAVA_HOME/bin/java"
    fi
    if [ ! -x "$JAVACMD" ] ; then
        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME

Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
    fi
else
    JAVACMD="java"
    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.

Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi

# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
    MAX_FD_LIMIT=`ulimit -H -n`
    if [ $? -eq 0 ] ; then
        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
            MAX_FD="$MAX_FD_LIMIT"
        fi
        ulimit -n $MAX_FD
        if [ $? -ne 0 ] ; then
            warn "Could not set maximum file descriptor limit: $MAX_FD"
        fi
    else
        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
    fi
fi

# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi

# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
    JAVACMD=`cygpath --unix "$JAVACMD"`

    # We build the pattern for arguments to be converted via cygpath
    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
    SEP=""
    for dir in $ROOTDIRSRAW ; do
        ROOTDIRS="$ROOTDIRS$SEP$dir"
        SEP="|"
    done
    OURCYGPATTERN="(^($ROOTDIRS))"
    # Add a user-defined pattern to the cygpath arguments
    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
    fi
    # Now convert the arguments - kludge to limit ourselves to /bin/sh
    i=0
    for arg in "$@" ; do
        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option

        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
        else
            eval `echo args$i`="\"$arg\""
        fi
        i=$((i+1))
    done
    case $i in
        (0) set -- ;;
        (1) set -- "$args0" ;;
        (2) set -- "$args0" "$args1" ;;
        (3) set -- "$args0" "$args1" "$args2" ;;
        (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
        (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
        (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
        (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
        (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
        (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
    esac
fi

# Escape application args
save () {
    for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
    echo " "
}
APP_ARGS=$(save "$@")

# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"

# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
  cd "$(dirname "$0")"
fi

exec "$JAVACMD" "$@"


================================================
FILE: gradlew.bat
================================================
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem  Gradle startup script for Windows
@rem
@rem ##########################################################################

@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal

set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%

@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=

@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome

set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init

echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.

goto fail

:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe

if exist "%JAVA_EXE%" goto init

echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.

goto fail

:init
@rem Get command-line arguments, handling Windows variants

if not "%OS%" == "Windows_NT" goto win9xME_args

:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2

:win9xME_args_slurp
if "x%~1" == "x" goto execute

set CMD_LINE_ARGS=%*

:execute
@rem Setup the command line

set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar

@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%

:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd

:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1

:mainEnd
if "%OS%"=="Windows_NT" endlocal

:omega


================================================
FILE: settings.gradle
================================================
include ':app', ':chartmodule', ':simulateddata'
rootProject.name='StockChart'


================================================
FILE: simulateddata/.gitignore
================================================
/build


================================================
FILE: simulateddata/build.gradle
================================================
apply plugin: 'com.android.library'

android {
  compileSdkVersion 29
  buildToolsVersion "29.0.2"

  defaultConfig {
    minSdkVersion 19
    targetSdkVersion 29
    versionCode 1
    versionName "1.0"

    testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    consumerProguardFiles 'consumer-rules.pro'
  }

  buildTypes {
    release {
      minifyEnabled false
      proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
    }
  }
}

dependencies {
  implementation fileTree(dir: 'libs', include: ['*.jar'])

  implementation 'androidx.appcompat:appcompat:1.0.2'
  implementation 'com.squareup.duktape:duktape-android:1.3.0'
}


================================================
FILE: simulateddata/consumer-rules.pro
================================================


================================================
FILE: simulateddata/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
#   http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
#   public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile


================================================
FILE: simulateddata/src/main/AndroidManifest.xml
================================================
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  package="com.guannan.simulateddata" />


================================================
FILE: simulateddata/src/main/assets/geli.json
================================================
[{"day":"1996-11-18T00:00:00.000Z","open":17.5,"high":55,"low":17.5,"close":50,"volume":6278300},{"day":"1996-11-19T00:00:00.000Z","open":49.2,"high":49.48,"low":46.99,"close":48.8,"volume":1109400},{"day":"1996-11-20T00:00:00.000Z","open":48,"high":50.38,"low":47.5,"close":49.45,"volume":957300},{"day":"1996-11-21T00:00:00.000Z","open":49.5,"high":49.99,"low":47.9,"close":47.95,"volume":570000},{"day":"1996-11-22T00:00:00.000Z","open":47.3,"high":48.5,"low":47.18,"close":48.5,"volume":376200},{"day":"1996-11-25T00:00:00.000Z","open":48.8,"high":49,"low":47,"close":48,"volume":413500},{"day":"1996-11-26T00:00:00.000Z","open":48.3,"high":48.5,"low":47.7,"close":47.86,"volume":310100},{"day":"1996-11-27T00:00:00.000Z","open":47.6,"high":47.7,"low":46.51,"close":46.9,"volume":515700},{"day":"1996-11-28T00:00:00.000Z","open":46.9,"high":48.88,"low":46.65,"close":48.1,"volume":341300},{"day":"1996-11-29T00:00:00.000Z","open":47.5,"high":48.6,"low":47.5,"close":47.8,"volume":115000},{"day":"1996-12-02T00:00:00.000Z","open":47.5,"high":48.5,"low":47.5,"close":47.93,"volume":231900},{"day":"1996-12-03T00:00:00.000Z","open":47.58,"high":48.2,"low":47.11,"close":48.2,"volume":224300},{"day":"1996-12-04T00:00:00.000Z","open":48.5,"high":50,"low":48,"close":49.31,"volume":607900},{"day":"1996-12-05T00:00:00.000Z","open":49.3,"high":50,"low":49,"close":49.61,"volume":443800},{"day":"1996-12-06T00:00:00.000Z","open":49.6,"high":49.75,"low":49.03,"close":49.5,"volume":432300},{"day":"1996-12-09T00:00:00.000Z","open":49.82,"high":60,"low":49.6,"close":59.4,"volume":1343300},{"day":"1996-12-10T00:00:00.000Z","open":60,"high":70,"low":58,"close":68.1,"volume":1986600},{"day":"1996-12-11T00:00:00.000Z","open":69.5,"high":78,"low":69,"close":76,"volume":2278900},{"day":"1996-12-12T00:00:00.000Z","open":75.8,"high":75.8,"low":69.5,"close":70.45,"volume":1208300},{"day":"1996-12-13T00:00:00.000Z","open":69.5,"high":73,"low":68,"close":68,"volume":1008600},{"day":"1996-12-16T00:00:00.000Z","open":61.26,"high":61.26,"low":61.26,"close":61.26,"volume":64700},{"day":"1996-12-17T00:00:00.000Z","open":55.13,"high":61.1,"low":55.13,"close":55.13,"volume":1360300},{"day":"1996-12-18T00:00:00.000Z","open":53,"high":60.64,"low":51,"close":60.64,"volume":2231500},{"day":"1996-12-19T00:00:00.000Z","open":61.5,"high":64.2,"low":56.1,"close":59.96,"volume":2297900},{"day":"1996-12-23T00:00:00.000Z","open":64.81,"high":64.9,"low":60,"close":63.4,"volume":1345700},{"day":"1996-12-24T00:00:00.000Z","open":65,"high":69.47,"low":65,"close":68.8,"volume":3724200},{"day":"1996-12-25T00:00:00.000Z","open":69.81,"high":70.99,"low":67,"close":70.61,"volume":2093200},{"day":"1996-12-26T00:00:00.000Z","open":71,"high":71.8,"low":69.35,"close":69.5,"volume":664400},{"day":"1996-12-27T00:00:00.000Z","open":70,"high":72,"low":68.5,"close":71.5,"volume":1818600},{"day":"1996-12-30T00:00:00.000Z","open":36.8,"high":39,"low":36.8,"close":38.99,"volume":4098400},{"day":"1996-12-31T00:00:00.000Z","open":39.8,"high":40,"low":37.83,"close":38.5,"volume":2141500},{"day":"1997-01-02T00:00:00.000Z","open":37.5,"high":38,"low":36.03,"close":36.81,"volume":3345700},{"day":"1997-01-03T00:00:00.000Z","open":36.5,"high":36.8,"low":35.98,"close":36.07,"volume":1931100},{"day":"1997-01-06T00:00:00.000Z","open":35.2,"high":35.47,"low":32.45,"close":32.45,"volume":2145000},{"day":"1997-01-07T00:00:00.000Z","open":31,"high":33.8,"low":30,"close":33.16,"volume":1767800},{"day":"1997-01-08T00:00:00.000Z","open":33.1,"high":33.5,"low":31.75,"close":32.4,"volume":939100},{"day":"1997-01-09T00:00:00.000Z","open":32,"high":32.8,"low":32,"close":32.2,"volume":571600},{"day":"1997-01-10T00:00:00.000Z","open":32.2,"high":34.6,"low":32,"close":33.73,"volume":1651000},{"day":"1997-01-13T00:00:00.000Z","open":33.96,"high":34.6,"low":33.65,"close":33.9,"volume":877100},{"day":"1997-01-14T00:00:00.000Z","open":33.8,"high":33.86,"low":32.45,"close":32.8,"volume":873400},{"day":"1997-01-15T00:00:00.000Z","open":32.2,"high":33,"low":32.2,"close":32.71,"volume":422000},{"day":"1997-01-16T00:00:00.000Z","open":32.61,"high":33,"low":32.55,"close":32.7,"volume":398000},{"day":"1997-01-17T00:00:00.000Z","open":32.7,"high":33,"low":32.6,"close":32.95,"volume":385400},{"day":"1997-01-20T00:00:00.000Z","open":32.92,"high":33.4,"low":32.5,"close":32.92,"volume":498700},{"day":"1997-01-21T00:00:00.000Z","open":33.3,"high":33.35,"low":32.7,"close":32.78,"volume":398700},{"day":"1997-01-22T00:00:00.000Z","open":33.01,"high":34.28,"low":33,"close":33.46,"volume":908600},{"day":"1997-01-23T00:00:00.000Z","open":33.48,"high":33.75,"low":33.01,"close":33.52,"volume":865600},{"day":"1997-01-24T00:00:00.000Z","open":33.3,"high":33.49,"low":33.01,"close":33.2,"volume":488200},{"day":"1997-01-27T00:00:00.000Z","open":33.25,"high":34.59,"low":33,"close":34.18,"volume":1208400},{"day":"1997-01-28T00:00:00.000Z","open":34.5,"high":34.5,"low":33.65,"close":33.7,"volume":590700},{"day":"1997-01-29T00:00:00.000Z","open":33.75,"high":33.75,"low":33.33,"close":33.43,"volume":443300},{"day":"1997-01-30T00:00:00.000Z","open":33.5,"high":33.6,"low":33.2,"close":33.36,"volume":609400},{"day":"1997-01-31T00:00:00.000Z","open":33.38,"high":34.3,"low":33.38,"close":33.65,"volume":690300},{"day":"1997-02-17T00:00:00.000Z","open":33.7,"high":34.5,"low":33.7,"close":33.98,"volume":595600},{"day":"1997-02-18T00:00:00.000Z","open":33.8,"high":33.96,"low":30.56,"close":30.56,"volume":1179700},{"day":"1997-02-19T00:00:00.000Z","open":30.66,"high":32.5,"low":30.65,"close":32.26,"volume":764400},{"day":"1997-02-20T00:00:00.000Z","open":29.03,"high":31.95,"low":29.03,"close":31.22,"volume":2034400},{"day":"1997-02-21T00:00:00.000Z","open":31.91,"high":32,"low":31.25,"close":31.71,"volume":705100},{"day":"1997-02-24T00:00:00.000Z","open":31.8,"high":31.8,"low":31.1,"close":31.4,"volume":728600},{"day":"1997-02-25T00:00:00.000Z","open":31.5,"high":32.16,"low":31.4,"close":31.6,"volume":559000},{"day":"1997-02-26T00:00:00.000Z","open":31.5,"high":33,"low":31.4,"close":32.7,"volume":852700},{"day":"1997-02-27T00:00:00.000Z","open":33,"high":33.5,"low":32.7,"close":32.88,"volume":1069700},{"day":"1997-02-28T00:00:00.000Z","open":32.88,"high":33.18,"low":32.45,"close":33,"volume":800500},{"day":"1997-03-03T00:00:00.000Z","open":33.4,"high":35,"low":33.09,"close":34.95,"volume":2519100},{"day":"1997-03-04T00:00:00.000Z","open":35,"high":35.5,"low":34.1,"close":34.53,"volume":1407100},{"day":"1997-03-05T00:00:00.000Z","open":34.3,"high":34.5,"low":33.45,"close":33.48,"volume":822900},{"day":"1997-03-06T00:00:00.000Z","open":32.98,"high":33.95,"low":32.8,"close":33.6,"volume":482000},{"day":"1997-03-07T00:00:00.000Z","open":33.8,"high":34,"low":33.28,"close":33.55,"volume":624400},{"day":"1997-03-10T00:00:00.000Z","open":33.6,"high":34.9,"low":33.5,"close":34,"volume":833500},{"day":"1997-03-11T00:00:00.000Z","open":34.1,"high":34.8,"low":34,"close":34.4,"volume":1140100},{"day":"1997-03-12T00:00:00.000Z","open":34.4,"high":37.85,"low":34.38,"close":37.85,"volume":5970900},{"day":"1997-03-13T00:00:00.000Z","open":39.8,"high":41,"low":38.88,"close":39.22,"volume":6607200},{"day":"1997-03-14T00:00:00.000Z","open":39,"high":39.25,"low":37.5,"close":39.22,"volume":2931600},{"day":"1997-03-17T00:00:00.000Z","open":40,"high":41,"low":39.5,"close":39.95,"volume":3049200},{"day":"1997-03-18T00:00:00.000Z","open":40.2,"high":40.5,"low":38.6,"close":39.4,"volume":2178400},{"day":"1997-03-19T00:00:00.000Z","open":39.4,"high":39.41,"low":38.6,"close":39.05,"volume":1302100},{"day":"1997-03-20T00:00:00.000Z","open":39.2,"high":39.3,"low":37.61,"close":37.8,"volume":1283100},{"day":"1997-03-21T00:00:00.000Z","open":37.2,"high":40.5,"low":37,"close":39.3,"volume":1891900},{"day":"1997-03-24T00:00:00.000Z","open":40.39,"high":42,"low":39.35,"close":41.98,"volume":3792500},{"day":"1997-03-25T00:00:00.000Z","open":42.5,"high":42.5,"low":40.2,"close":41,"volume":2784000},{"day":"1997-03-26T00:00:00.000Z","open":40.9,"high":41,"low":40,"close":40.39,"volume":1493600},{"day":"1997-03-27T00:00:00.000Z","open":40.3,"high":42,"low":39.9,"close":40.61,"volume":2203300},{"day":"1997-03-28T00:00:00.000Z","open":40.11,"high":40.84,"low":40,"close":40.06,"volume":1128800},{"day":"1997-03-31T00:00:00.000Z","open":40,"high":41.48,"low":40,"close":40.4,"volume":1071400},{"day":"1997-04-01T00:00:00.000Z","open":40.5,"high":40.78,"low":40,"close":40.2,"volume":932100},{"day":"1997-04-02T00:00:00.000Z","open":40.03,"high":41,"low":40.03,"close":40.18,"volume":896400},{"day":"1997-04-03T00:00:00.000Z","open":40.1,"high":40.6,"low":39.6,"close":40.25,"volume":847100},{"day":"1997-04-04T00:00:00.000Z","open":40.5,"high":42.3,"low":40.5,"close":41.45,"volume":2657600},{"day":"1997-04-07T00:00:00.000Z","open":42.3,"high":45.65,"low":42,"close":45.65,"volume":4776200},{"day":"1997-04-08T00:00:00.000Z","open":46,"high":46.3,"low":43.5,"close":43.54,"volume":2774400},{"day":"1997-04-09T00:00:00.000Z","open":43.3,"high":44.25,"low":43,"close":43.95,"volume":1558200},{"day":"1997-04-10T00:00:00.000Z","open":44,"high":44,"low":42.5,"close":42.54,"volume":1697600},{"day":"1997-04-11T00:00:00.000Z","open":42,"high":42,"low":40.8,"close":41.28,"volume":1961100},{"day":"1997-04-14T00:00:00.000Z","open":41.11,"high":42.4,"low":41.11,"close":41.28,"volume":963100},{"day":"1997-04-15T00:00:00.000Z","open":41.2,"high":41.5,"low":40.88,"close":40.98,"volume":880300},{"day":"1997-04-16T00:00:00.000Z","open":41.13,"high":42,"low":40.8,"close":41.25,"volume":968900},{"day":"1997-04-17T00:00:00.000Z","open":41.4,"high":41.5,"low":41,"close":41.1,"volume":695500},{"day":"1997-04-18T00:00:00.000Z","open":41.1,"high":41.28,"low":40.25,"close":40.65,"volume":1143000},{"day":"1997-04-21T00:00:00.000Z","open":40.49,"high":41.9,"low":40.35,"close":41.15,"volume":1543900},{"day":"1997-04-22T00:00:00.000Z","open":41.5,"high":41.5,"low":40.95,"close":41.15,"volume":975400},{"day":"1997-04-23T00:00:00.000Z","open":41.15,"high":44,"low":40.8,"close":42.6,"volume":2504600},{"day":"1997-04-24T00:00:00.000Z","open":42.6,"high":43.99,"low":41.5,"close":43.3,"volume":2516500},{"day":"1997-04-25T00:00:00.000Z","open":43.6,"high":45.8,"low":43.5,"close":44.4,"volume":3868800},{"day":"1997-04-28T00:00:00.000Z","open":40,"high":45,"low":39.95,"close":41.49,"volume":3825500},{"day":"1997-04-29T00:00:00.000Z","open":41,"high":41.8,"low":39.8,"close":40.31,"volume":3039600},{"day":"1997-04-30T00:00:00.000Z","open":40.2,"high":41.55,"low":39.88,"close":41.05,"volume":1562600},{"day":"1997-05-05T00:00:00.000Z","open":41.28,"high":43,"low":41.2,"close":42.25,"volume":2228900},{"day":"1997-05-06T00:00:00.000Z","open":42.5,"high":44.6,"low":42.5,"close":44.1,"volume":3606700},{"day":"1997-05-07T00:00:00.000Z","open":44.48,"high":48.52,"low":44.4,"close":48.52,"volume":4818100},{"day":"1997-05-08T00:00:00.000Z","open":50,"high":53.37,"low":45.5,"close":46.5,"volume":6868700},{"day":"1997-05-09T00:00:00.000Z","open":45.8,"high":50.28,"low":45.6,"close":49.98,"volume":4962900},{"day":"1997-05-12T00:00:00.000Z","open":51,"high":53,"low":48,"close":48.8,"volume":3479900},{"day":"1997-05-13T00:00:00.000Z","open":48.5,"high":49,"low":46,"close":47.04,"volume":2398100},{"day":"1997-05-14T00:00:00.000Z","open":47,"high":47.2,"low":44,"close":44.63,"volume":1942600},{"day":"1997-05-15T00:00:00.000Z","open":44,"high":46.1,"low":43.3,"close":45.88,"volume":1515100},{"day":"1997-05-16T00:00:00.000Z","open":44,"high":44.9,"low":41.26,"close":41.29,"volume":2092100},{"day":"1997-05-19T00:00:00.000Z","open":41,"high":43,"low":41,"close":42.51,"volume":1069500},{"day":"1997-05-20T00:00:00.000Z","open":43,"high":45.27,"low":43,"close":44.8,"volume":2012500},{"day":"1997-05-21T00:00:00.000Z","open":44.86,"high":45.3,"low":42.85,"close":43.2,"volume":1295100},{"day":"1997-05-22T00:00:00.000Z","open":38.88,"high":42,"low":38.88,"close":39.59,"volume":2331500},{"day":"1997-05-23T00:00:00.000Z","open":39.59,"high":41.66,"low":39.59,"close":41.33,"volume":1336900},{"day":"1997-05-26T00:00:00.000Z","open":42,"high":42.6,"low":40,"close":41.75,"volume":955800},{"day":"1997-05-27T00:00:00.000Z","open":42.3,"high":42.5,"low":41.56,"close":41.88,"volume":778700},{"day":"1997-05-28T00:00:00.000Z","open":41.9,"high":42.65,"low":41.7,"close":41.79,"volume":750800},{"day":"1997-05-30T00:00:00.000Z","open":41.1,"high":42.2,"low":40.1,"close":41.63,"volume":443300},{"day":"1997-06-02T00:00:00.000Z","open":42,"high":42.5,"low":41.76,"close":42.42,"volume":1032300},{"day":"1997-06-03T00:00:00.000Z","open":42.52,"high":42.88,"low":41.5,"close":41.72,"volume":911500},{"day":"1997-06-04T00:00:00.000Z","open":41.6,"high":42.48,"low":41.2,"close":42,"volume":685900},{"day":"1997-06-05T00:00:00.000Z","open":42.3,"high":43.8,"low":42.2,"close":42.66,"volume":1022700},{"day":"1997-06-06T00:00:00.000Z","open":38.8,"high":41.5,"low":38.8,"close":40.04,"volume":1472100},{"day":"1997-06-09T00:00:00.000Z","open":40.5,"high":40.7,"low":39.5,"close":39.78,"volume":459000},{"day":"1997-06-10T00:00:00.000Z","open":39.98,"high":40,"low":38.68,"close":38.88,"volume":457600},{"day":"1997-06-11T00:00:00.000Z","open":38.6,"high":38.6,"low":35.95,"close":36.87,"volume":805600},{"day":"1997-06-12T00:00:00.000Z","open":37.55,"high":38,"low":37.1,"close":37.4,"volume":379900},{"day":"1997-06-13T00:00:00.000Z","open":36,"high":38.27,"low":35.5,"close":38.24,"volume":654200},{"day":"1997-06-16T00:00:00.000Z","open":39.2,"high":39.89,"low":39,"close":39.66,"volume":470500},{"day":"1997-06-17T00:00:00.000Z","open":39.68,"high":40,"low":38.65,"close":38.88,"volume":374700},{"day":"1997-06-18T00:00:00.000Z","open":39.5,"high":39.6,"low":38.38,"close":38.45,"volume":235000},{"day":"1997-06-19T00:00:00.000Z","open":39,"high":39,"low":37.9,"close":38.21,"volume":292300},{"day":"1997-06-20T00:00:00.000Z","open":39,"high":41,"low":38.5,"close":40.81,"volume":755100},{"day":"1997-06-23T00:00:00.000Z","open":41.8,"high":43.65,"low":40.2,"close":41.59,"volume":1417800},{"day":"1997-06-24T00:00:00.000Z","open":41.95,"high":42.5,"low":41.66,"close":42.05,"volume":531400},{"day":"1997-06-25T00:00:00.000Z","open":42.3,"high":42.5,"low":41,"close":41.7,"volume":601800},{"day":"1997-06-26T00:00:00.000Z","open":41.7,"high":42,"low":41.01,"close":41.78,"volume":666000},{"day":"1997-06-27T00:00:00.000Z","open":42,"high":44.68,"low":42,"close":43.26,"volume":2628500},{"day":"1997-07-02T00:00:00.000Z","open":44.29,"high":44.98,"low":40.2,"close":43.22,"volume":1253500},{"day":"1997-07-03T00:00:00.000Z","open":43.5,"high":43.5,"low":41.5,"close":41.78,"volume":781000},{"day":"1997-07-04T00:00:00.000Z","open":41,"high":42,"low":40.41,"close":41.56,"volume":589400},{"day":"1997-07-07T00:00:00.000Z","open":41.5,"high":42.4,"low":38.6,"close":39.1,"volume":659400},{"day":"1997-07-08T00:00:00.000Z","open":39,"high":40.5,"low":38.1,"close":40.05,"volume":644900},{"day":"1997-07-09T00:00:00.000Z","open":41,"high":41,"low":39.9,"close":40.48,"volume":360500},{"day":"1997-07-10T00:00:00.000Z","open":40.1,"high":41.38,"low":40,"close":40.08,"volume":324100},{"day":"1997-07-11T00:00:00.000Z","open":40.05,"high":41.6,"low":40,"close":41.47,"volume":455000},{"day":"1997-07-14T00:00:00.000Z","open":42.11,"high":43.5,"low":42,"close":42.47,"volume":696700},{"day":"1997-07-15T00:00:00.000Z","open":43.49,"high":43.49,"low":42.18,"close":42.61,"volume":529800},{"day":"1997-07-16T00:00:00.000Z","open":42.5,"high":43.1,"low":42.1,"close":42.24,"volume":361100},{"day":"1997-07-17T00:00:00.000Z","open":42.3,"high":43,"low":42,"close":42.29,"volume":448000},{"day":"1997-07-18T00:00:00.000Z","open":42.42,"high":42.8,"low":42.28,"close":42.65,"volume":408600},{"day":"1997-07-21T00:00:00.000Z","open":42.8,"high":43,"low":41.61,"close":41.86,"volume":370000},{"day":"1997-07-22T00:00:00.000Z","open":42.3,"high":42.3,"low":40.5,"close":41.9,"volume":300100},{"day":"1997-07-23T00:00:00.000Z","open":42.25,"high":42.6,"low":41.5,"close":41.72,"volume":358800},{"day":"1997-07-24T00:00:00.000Z","open":40.61,"high":41.5,"low":40.2,"close":40.7,"volume":264200},{"day":"1997-07-25T00:00:00.000Z","open":40.29,"high":40.98,"low":40.2,"close":40.56,"volume":356600},{"day":"1997-07-28T00:00:00.000Z","open":40.5,"high":40.5,"low":39,"close":39.54,"volume":507800},{"day":"1997-07-29T00:00:00.000Z","open":39.1,"high":40,"low":38.5,"close":39.4,"volume":230900},{"day":"1997-07-30T00:00:00.000Z","open":39.99,"high":39.99,"low":39.39,"close":39.6,"volume":217900},{"day":"1997-07-31T00:00:00.000Z","open":39.6,"high":40.3,"low":39.6,"close":39.87,"volume":183200},{"day":"1997-08-01T00:00:00.000Z","open":40,"high":40.3,"low":39.6,"close":39.6,"volume":183500},{"day":"1997-08-04T00:00:00.000Z","open":40.75,"high":42.2,"low":40.1,"close":40.5,"volume":279000},{"day":"1997-08-05T00:00:00.000Z","open":41.5,"high":41.5,"low":40.35,"close":40.47,"volume":202100},{"day":"1997-08-06T00:00:00.000Z","open":40.4,"high":40.5,"low":38,"close":38.8,"volume":209300},{"day":"1997-08-07T00:00:00.000Z","open":38.59,"high":39.6,"low":38.55,"close":39.14,"volume":264700},{"day":"1997-08-08T00:00:00.000Z","open":39.98,"high":40.5,"low":39.55,"close":40.1,"volume":386300},{"day":"1997-08-11T00:00:00.000Z","open":40.41,"high":40.8,"low":39.5,"close":39.6,"volume":323100},{"day":"1997-08-12T00:00:00.000Z","open":39,"high":39.2,"low":38,"close":38,"volume":301700},{"day":"1997-08-13T00:00:00.000Z","open":37.51,"high":38.2,"low":36,"close":36.38,"volume":287800},{"day":"1997-08-14T00:00:00.000Z","open":36.1,"high":38.5,"low":36,"close":38.34,"volume":296400},{"day":"1997-08-15T00:00:00.000Z","open":38.5,"high":38.5,"low":37.01,"close":37.4,"volume":154400},{"day":"1997-08-18T00:00:00.000Z","open":37.2,"high":37.2,"low":36.5,"close":36.8,"volume":173200},{"day":"1997-08-19T00:00:00.000Z","open":37.2,"high":37.2,"low":36.5,"close":36.7,"volume":105500},{"day":"1997-08-20T00:00:00.000Z","open":36.65,"high":37,"low":36.5,"close":36.64,"volume":165900},{"day":"1997-08-21T00:00:00.000Z","open":36.5,"high":38.48,"low":36.4,"close":37.92,"volume":314900},{"day":"1997-08-22T00:00:00.000Z","open":41.7,"high":41.7,"low":39.5,"close":40,"volume":1432600},{"day":"1997-08-25T00:00:00.000Z","open":40.04,"high":40.4,"low":38.82,"close":39.05,"volume":731500},{"day":"1997-08-26T00:00:00.000Z","open":39.04,"high":40,"low":38.94,"close":39.33,"volume":398200},{"day":"1997-08-27T00:00:00.000Z","open":39.32,"high":39.35,"low":38.85,"close":38.97,"volume":272400},{"day":"1997-08-28T00:00:00.000Z","open":38.93,"high":38.97,"low":38.3,"close":38.47,"volume":311700},{"day":"1997-08-29T00:00:00.000Z","open":38.4,"high":39.18,"low":38.2,"close":38.4,"volume":278200},{"day":"1997-09-01T00:00:00.000Z","open":38.3,"high":39.05,"low":38,"close":38.31,"volume":223600},{"day":"1997-09-02T00:00:00.000Z","open":38.1,"high":39,"low":38.1,"close":38.5,"volume":172600},{"day":"1997-09-03T00:00:00.000Z","open":38.4,"high":39.15,"low":38.4,"close":39.1,"volume":236100},{"day":"1997-09-04T00:00:00.000Z","open":39.38,"high":39.38,"low":38.79,"close":38.81,"volume":235900},{"day":"1997-09-05T00:00:00.000Z","open":38.5,"high":38.91,"low":38.3,"close":38.38,"volume":196900},{"day":"1997-09-08T00:00:00.000Z","open":38.4,"high":38.55,"low":37.8,"close":37.95,"volume":380400},{"day":"1997-09-09T00:00:00.000Z","open":37.96,"high":38.2,"low":37.75,"close":37.75,"volume":154300},{"day":"1997-09-10T00:00:00.000Z","open":37.75,"high":37.75,"low":37.25,"close":37.36,"volume":237700},{"day":"1997-09-11T00:00:00.000Z","open":37.36,"high":39.1,"low":37.36,"close":38.11,"volume":377000},{"day":"1997-09-12T00:00:00.000Z","open":38.95,"high":38.95,"low":37.58,"close":37.6,"volume":136200},{"day":"1997-09-15T00:00:00.000Z","open":37.6,"high":37.6,"low":36.8,"close":36.85,"volume":178500},{"day":"1997-09-16T00:00:00.000Z","open":37,"high":37.25,"low":36.61,"close":36.9,"volume":113400},{"day":"1997-09-17T00:00:00.000Z","open":37,"high":37.13,"low":36.5,"close":36.87,"volume":122800},{"day":"1997-09-18T00:00:00.000Z","open":36.9,"high":37.1,"low":36.62,"close":36.81,"volume":81400},{"day":"1997-09-19T00:00:00.000Z","open":37,"high":38.02,"low":36.8,"close":37.1,"volume":105500},{"day":"1997-09-23T00:00:00.000Z","open":34,"high":34.51,"low":33.39,"close":33.39,"volume":230700},{"day":"1997-09-24T00:00:00.000Z","open":32.89,"high":34.48,"low":32.5,"close":34.16,"volume":678300},{"day":"1997-09-25T00:00:00.000Z","open":34.5,"high":34.8,"low":33.3,"close":33.55,"volume":356600},{"day":"1997-09-26T00:00:00.000Z","open":33.7,"high":33.71,"low":32.8,"close":33,"volume":202100},{"day":"1997-09-29T00:00:00.000Z","open":32.99,"high":32.99,"low":30.5,"close":31.41,"volume":269100},{"day":"1997-09-30T00:00:00.000Z","open":31.5,"high":32.2,"low":31,"close":31.89,"volume":145200},{"day":"1997-10-06T00:00:00.000Z","open":31.99,"high":32.2,"low":31.5,"close":31.63,"volume":103500},{"day":"1997-10-07T00:00:00.000Z","open":31.55,"high":31.75,"low":31.1,"close":31.25,"volume":67900},{"day":"1997-10-08T00:00:00.000Z","open":32,"high":32.55,"low":31,"close":32.11,"volume":151900},{"day":"1997-10-09T00:00:00.000Z","open":32.11,"high":32.2,"low":31.9,"close":31.9,"volume":121000},{"day":"1997-10-10T00:00:00.000Z","open":32,"high":32.2,"low":31.9,"close":32,"volume":102100},{"day":"1997-10-13T00:00:00.000Z","open":32.01,"high":32.15,"low":31.6,"close":31.6,"volume":87800},{"day":"1997-10-14T00:00:00.000Z","open":31.6,"high":31.6,"low":31.1,"close":31.3,"volume":81300},{"day":"1997-10-15T00:00:00.000Z","open":31.8,"high":31.8,"low":31.01,"close":31.55,"volume":93100},{"day":"1997-10-16T00:00:00.000Z","open":32,"high":33.7,"low":31.66,"close":33.12,"volume":379300},{"day":"1997-10-17T00:00:00.000Z","open":33.69,"high":34.68,"low":33.5,"close":34.41,"volume":553300},{"day":"1997-10-20T00:00:00.000Z","open":34.78,"high":35.2,"low":34,"close":34.29,"volume":461800},{"day":"1997-10-21T00:00:00.000Z","open":34.29,"high":35.15,"low":33.86,"close":35,"volume":453900},{"day":"1997-10-22T00:00:00.000Z","open":35.26,"high":36.35,"low":34.5,"close":34.7,"volume":898700},{"day":"1997-10-23T00:00:00.000Z","open":35,"high":35.3,"low":34.35,"close":35.15,"volume":527700},{"day":"1997-10-24T00:00:00.000Z","open":35,"high":35.18,"low":34.5,"close":35.13,"volume":434300},{"day":"1997-10-27T00:00:00.000Z","open":35.14,"high":36.2,"low":34.8,"close":35.43,"volume":540400},{"day":"1997-10-28T00:00:00.000Z","open":35.5,"high":35.52,"low":33.1,"close":33.92,"volume":464700},{"day":"1997-10-29T00:00:00.000Z","open":33.7,"high":34.6,"low":33,"close":34.56,"volume":359900},{"day":"1997-10-30T00:00:00.000Z","open":34.7,"high":37.03,"low":34.7,"close":35.81,"volume":1131400},{"day":"1997-10-31T00:00:00.000Z","open":35.7,"high":35.7,"low":34.4,"close":35.27,"volume":316800},{"day":"1997-11-03T00:00:00.000Z","open":35.5,"high":35.85,"low":34.58,"close":34.8,"volume":170500},{"day":"1997-11-04T00:00:00.000Z","open":34.6,"high":35,"low":34.5,"close":34.79,"volume":164600},{"day":"1997-11-05T00:00:00.000Z","open":35.1,"high":35.3,"low":34.88,"close":35.3,"volume":308100},{"day":"1997-11-06T00:00:00.000Z","open":36,"high":36.18,"low":35.2,"close":35.21,"volume":239000},{"day":"1997-11-07T00:00:00.000Z","open":35.81,"high":38.73,"low":35.8,"close":38.73,"volume":1606800},{"day":"1997-11-10T00:00:00.000Z","open":40,"high":41,"low":38.5,"close":38.8,"volume":2398400},{"day":"1997-11-11T00:00:00.000Z","open":38.5,"high":38.8,"low":37.5,"close":38.02,"volume":574400},{"day":"1997-11-12T00:00:00.000Z","open":38.2,"high":38.5,"low":37,"close":37.03,"volume":331400},{"day":"1997-11-13T00:00:00.000Z","open":36.8,"high":37.5,"low":35.8,"close":37.35,"volume":423400},{"day":"1997-11-14T00:00:00.000Z","open":37.38,"high":38.4,"low":37.1,"close":37.45,"volume":386100},{"day":"1997-11-17T00:00:00.000Z","open":37.9,"high":38.2,"low":36.98,"close":37.07,"volume":341100},{"day":"1997-11-18T00:00:00.000Z","open":36,"high":36,"low":35,"close":35.3,"volume":282900},{"day":"1997-11-19T00:00:00.000Z","open":35.01,"high":35.15,"low":34.6,"close":34.9,"volume":231000},{"day":"1997-11-20T00:00:00.000Z","open":36,"high":36,"low":34.9,"close":35.1,"volume":190200},{"day":"1997-11-21T00:00:00.000Z","open":35.07,"high":35.07,"low":34.05,"close":34.1,"volume":274900},{"day":"1997-11-24T00:00:00.000Z","open":33,"high":34,"low":32.71,"close":33.16,"volume":197700},{"day":"1997-11-25T00:00:00.000Z","open":33,"high":33.5,"low":32.5,"close":33.11,"volume":210100},{"day":"1997-11-26T00:00:00.000Z","open":33.11,"high":34.5,"low":33.11,"close":34.05,"volume":130400},{"day":"1997-11-27T00:00:00.000Z","open":34,"high":34,"low":33.1,"close":33.2,"volume":124600},{"day":"1997-11-28T00:00:00.000Z","open":33,"high":33.8,"low":32.7,"close":33.75,"volume":132600},{"day":"1997-12-01T00:00:00.000Z","open":33.75,"high":33.75,"low":33.11,"close":33.31,"volume":108200},{"day":"1997-12-02T00:00:00.000Z","open":33.4,"high":33.75,"low":33.01,"close":33.5,"volume":62500},{"day":"1997-12-03T00:00:00.000Z","open":33.12,"high":33.5,"low":32.95,"close":32.99,"volume":165400},{"day":"1997-12-04T00:00:00.000Z","open":33.1,"high":34.45,"low":33.1,"close":33.69,"volume":235600},{"day":"1997-12-05T00:00:00.000Z","open":33.9,"high":34.4,"low":33.55,"close":33.58,"volume":232400},{"day":"1997-12-08T00:00:00.000Z","open":34.1,"high":34.18,"low":33.45,"close":33.56,"volume":158100},{"day":"1997-12-09T00:00:00.000Z","open":33.56,"high":34.2,"low":33.56,"close":34.09,"volume":129200},{"day":"1997-12-10T00:00:00.000Z","open":34.2,"high":35.01,"low":34.2,"close":34.9,"volume":234100},{"day":"1997-12-11T00:00:00.000Z","open":35.21,"high":35.21,"low":34.5,"close":35.05,"volume":194100},{"day":"1997-12-12T00:00:00.000Z","open":35.17,"high":35.25,"low":34.88,"close":34.9,"volume":181900},{"day":"1997-12-15T00:00:00.000Z","open":34.55,"high":35.15,"low":34.55,"close":34.91,"volume":155800},{"day":"1997-12-16T00:00:00.000Z","open":35.3,"high":36.8,"low":35.3,"close":36.59,"volume":478400},{"day":"1997-12-17T00:00:00.000Z","open":36.81,"high":36.98,"low":35.88,"close":35.88,"volume":448100},{"day":"1997-12-18T00:00:00.000Z","open":35.8,"high":36.8,"low":35.3,"close":36.46,"volume":460900},{"day":"1997-12-19T00:00:00.000Z","open":36.51,"high":38.28,"low":36.51,"close":37.86,"volume":892600},{"day":"1997-12-22T00:00:00.000Z","open":38.55,"high":39.5,"low":38.55,"close":38.89,"volume":977000},{"day":"1997-12-24T00:00:00.000Z","open":38,"high":39.6,"low":37.88,"close":38.1,"volume":686100},{"day":"1997-12-25T00:00:00.000Z","open":37.96,"high":38.5,"low":37.2,"close":37.38,"volume":687800},{"day":"1997-12-26T00:00:00.000Z","open":37.81,"high":38.4,"low":37.56,"close":37.63,"volume":497400},{"day":"1997-12-29T00:00:00.000Z","open":37.63,"high":38,"low":36.88,"close":36.98,"volume":603800},{"day":"1997-12-30T00:00:00.000Z","open":36.88,"high":37.05,"low":36,"close":36.64,"volume":352500},{"day":"1997-12-31T00:00:00.000Z","open":36.46,"high":38.2,"low":36.46,"close":37.93,"volume":326300},{"day":"1998-01-05T00:00:00.000Z","open":38.4,"high":39.8,"low":37.7,"close":39.51,"volume":1067600},{"day":"1998-01-06T00:00:00.000Z","open":39.95,"high":41.3,"low":39,"close":40.07,"volume":1196500},{"day":"1998-01-07T00:00:00.000Z","open":39.6,"high":40.6,"low":39.52,"close":40.1,"volume":617300},{"day":"1998-01-08T00:00:00.000Z","open":40.1,"high":43.6,"low":39.7,"close":42.89,"volume":1893200},{"day":"1998-01-09T00:00:00.000Z","open":43.2,"high":47.18,"low":43.06,"close":45.86,"volume":3654200},{"day":"1998-01-12T00:00:00.000Z","open":46.52,"high":48,"low":43.8,"close":44.15,"volume":1917100},{"day":"1998-01-13T00:00:00.000Z","open":43.1,"high":43.48,"low":39.74,"close":41.53,"volume":3335400},{"day":"1998-01-14T00:00:00.000Z","open":41.5,"high":44.38,"low":41.5,"close":43.2,"volume":1601900},{"day":"1998-01-15T00:00:00.000Z","open":44,"high":44.47,"low":42.01,"close":43.39,"volume":762100},{"day":"1998-01-16T00:00:00.000Z","open":43.3,"high":46.4,"low":42.8,"close":45.39,"volume":1977700},{"day":"1998-01-19T00:00:00.000Z","open":46.4,"high":48.5,"low":45.9,"close":47.81,"volume":3721300},{"day":"1998-01-20T00:00:00.000Z","open":43.03,"high":43.03,"low":43.03,"close":43.03,"volume":704900},{"day":"1998-01-21T00:00:00.000Z","open":38.75,"high":40.5,"low":38.73,"close":38.73,"volume":4323400},{"day":"1998-01-22T00:00:00.000Z","open":38.75,"high":39.5,"low":38,"close":38.57,"volume":1778300},{"day":"1998-01-23T00:00:00.000Z","open":38.35,"high":39.49,"low":37.8,"close":39,"volume":1653700},{"day":"1998-02-09T00:00:00.000Z","open":39.2,"high":39.6,"low":37.68,"close":38.13,"volume":900400},{"day":"1998-02-10T00:00:00.000Z","open":37.99,"high":38,"low":36.61,"close":36.71,"volume":770700},{"day":"1998-02-11T00:00:00.000Z","open":36.3,"high":37.48,"low":36.08,"close":36.73,"volume":473600},{"day":"1998-02-12T00:00:00.000Z","open":36.5,"high":37.4,"low":36.4,"close":36.91,"volume":363300},{"day":"1998-02-13T00:00:00.000Z","open":37,"high":37.39,"low":36.7,"close":36.99,"volume":397400},{"day":"1998-02-16T00:00:00.000Z","open":36.81,"high":37.2,"low":36.03,"close":36.3,"volume":517100},{"day":"1998-02-17T00:00:00.000Z","open":36.1,"high":36.49,"low":36,"close":36.11,"volume":437700},{"day":"1998-02-18T00:00:00.000Z","open":36.19,"high":37.2,"low":36.09,"close":36.98,"volume":341100},{"day":"1998-02-19T00:00:00.000Z","open":37,"high":37.39,"low":36.8,"close":37.11,"volume":268500},{"day":"1998-02-23T00:00:00.000Z","open":36.69,"high":36.69,"low":36,"close":36.11,"volume":376300},{"day":"1998-02-24T00:00:00.000Z","open":36.08,"high":36.48,"low":36,"close":36.06,"volume":238500},{"day":"1998-02-25T00:00:00.000Z","open":36.11,"high":36.11,"low":35,"close":35.05,"volume":391100},{"day":"1998-02-26T00:00:00.000Z","open":35.05,"high":35.99,"low":34.7,"close":35.79,"volume":458900},{"day":"1998-02-27T00:00:00.000Z","open":35.6,"high":35.88,"low":34.98,"close":35.4,"volume":328800},{"day":"1998-03-02T00:00:00.000Z","open":34.5,"high":35,"low":34.1,"close":34.13,"volume":388000},{"day":"1998-03-03T00:00:00.000Z","open":34,"high":34.6,"low":33.8,"close":34.57,"volume":209600},{"day":"1998-03-04T00:00:00.000Z","open":34.69,"high":35.75,"low":34.5,"close":35.7,"volume":249300},{"day":"1998-03-05T00:00:00.000Z","open":35.81,"high":36.29,"low":35,"close":35.22,"volume":197800},{"day":"1998-03-06T00:00:00.000Z","open":35.38,"high":35.38,"low":34.8,"close":35,"volume":164300},{"day":"1998-03-09T00:00:00.000Z","open":35,"high":35.59,"low":35,"close":35.3,"volume":185400},{"day":"1998-03-10T00:00:00.000Z","open":35.2,"high":35.5,"low":34.9,"close":35.39,"volume":194000},{"day":"1998-03-11T00:00:00.000Z","open":35.61,"high":36.2,"low":35,"close":35,"volume":282600},{"day":"1998-03-12T00:00:00.000Z","open":34.99,"high":35.12,"low":34.3,"close":34.86,"volume":168800},{"day":"1998-03-13T00:00:00.000Z","open":34.9,"high":35.2,"low":34.65,"close":35,"volume":138600},{"day":"1998-03-16T00:00:00.000Z","open":35,"high":35.4,"low":34.7,"close":35.2,"volume":220900},{"day":"1998-03-17T00:00:00.000Z","open":35.41,"high":35.84,"low":35.2,"close":35.46,"volume":204600},{"day":"1998-03-18T00:00:00.000Z","open":35.8,"high":35.8,"low":34.81,"close":34.9,"volume":190600},{"day":"1998-03-19T00:00:00.000Z","open":34.89,"high":35.39,"low":34.7,"close":34.8,"volume":152100},{"day":"1998-03-20T00:00:00.000Z","open":34.8,"high":35.2,"low":34.41,"close":34.5,"volume":197100},{"day":"1998-03-23T00:00:00.000Z","open":34.4,"high":34.77,"low":34,"close":34.04,"volume":234000},{"day":"1998-03-24T00:00:00.000Z","open":34.5,"high":34.6,"low":34,"close":34.6,"volume":268200},{"day":"1998-03-25T00:00:00.000Z","open":35.15,"high":35.5,"low":34.37,"close":34.48,"volume":258900},{"day":"1998-03-26T00:00:00.000Z","open":34.4,"high":34.48,"low":34,"close":34,"volume":319900},{"day":"1998-03-27T00:00:00.000Z","open":34,"high":34.98,"low":33.8,"close":34.61,"volume":495100},{"day":"1998-03-30T00:00:00.000Z","open":34.8,"high":35.3,"low":33.9,"close":35.27,"volume":645800},{"day":"1998-03-31T00:00:00.000Z","open":35.5,"high":35.98,"low":35.05,"close":35.64,"volume":607000},{"day":"1998-04-01T00:00:00.000Z","open":35.7,"high":35.75,"low":34.98,"close":35,"volume":333900},{"day":"1998-04-02T00:00:00.000Z","open":34.9,"high":35.5,"low":34.8,"close":35.26,"volume":547400},{"day":"1998-04-03T00:00:00.000Z","open":35.2,"high":37.28,"low":35.1,"close":36.32,"volume":758700},{"day":"1998-04-06T00:00:00.000Z","open":36.8,"high":37,"low":35.97,"close":36.02,"volume":565400},{"day":"1998-04-07T00:00:00.000Z","open":36.04,"high":36.1,"low":35.45,"close":35.58,"volume":429500},{"day":"1998-04-08T00:00:00.000Z","open":35.6,"high":36.59,"low":35.5,"close":35.9,"volume":297900},{"day":"1998-04-09T00:00:00.000Z","open":35.7,"high":36.3,"low":35,"close":35.75,"volume":450500},{"day":"1998-04-10T00:00:00.000Z","open":35.5,"high":36.6,"low":35.5,"close":36.52,"volume":578200},{"day":"1998-04-13T00:00:00.000Z","open":36.69,"high":37.8,"low":36.5,"close":37.4,"volume":858800},{"day":"1998-04-14T00:00:00.000Z","open":37.3,"high":37.3,"low":36.6,"close":36.7,"volume":350300},{"day":"1998-04-15T00:00:00.000Z","open":36.68,"high":37.3,"low":36.51,"close":36.77,"volume":323500},{"day":"1998-04-16T00:00:00.000Z","open":36.81,"high":36.81,"low":36,"close":36.01,"volume":383700},{"day":"1998-04-17T00:00:00.000Z","open":35.9,"high":36.5,"low":35.2,"close":36.47,"volume":510300},{"day":"1998-04-20T00:00:00.000Z","open":36.49,"high":36.5,"low":35.38,"close":35.41,"volume":1080700},{"day":"1998-04-21T00:00:00.000Z","open":33.8,"high":34,"low":33,"close":33.49,"volume":459700},{"day":"1998-04-22T00:00:00.000Z","open":33.47,"high":33.94,"low":33.05,"close":33.6,"volume":415000},{"day":"1998-04-23T00:00:00.000Z","open":33.65,"high":34.2,"low":33.5,"close":33.96,"volume":354800},{"day":"1998-04-24T00:00:00.000Z","open":33.8,"high":36.18,"low":33.55,"close":34.85,"volume":803600},{"day":"1998-04-27T00:00:00.000Z","open":34.79,"high":35.53,"low":34,"close":34.94,"volume":459500},{"day":"1998-04-28T00:00:00.000Z","open":35,"high":35.39,"low":34.91,"close":35.06,"volume":362600},{"day":"1998-04-29T00:00:00.000Z","open":35.2,"high":35.4,"low":34.76,"close":34.96,"volume":272400},{"day":"1998-04-30T00:00:00.000Z","open":34.95,"high":35,"low":34.65,"close":34.77,"volume":304100},{"day":"1998-05-04T00:00:00.000Z","open":35,"high":36,"low":34.9,"close":35.72,"volume":712600},{"day":"1998-05-05T00:00:00.000Z","open":35.95,"high":36.06,"low":35.4,"close":35.41,"volume":486700},{"day":"1998-05-06T00:00:00.000Z","open":35.38,"high":35.38,"low":34.62,"close":34.74,"volume":395400},{"day":"1998-05-07T00:00:00.000Z","open":34,"high":35.68,"low":34,"close":34.42,"volume":487500},{"day":"1998-05-08T00:00:00.000Z","open":34.21,"high":35.5,"low":34.21,"close":35.08,"volume":481000},{"day":"1998-05-11T00:00:00.000Z","open":35,"high":35.4,"low":34.32,"close":34.73,"volume":458300},{"day":"1998-05-12T00:00:00.000Z","open":34.5,"high":34.68,"low":34.2,"close":34.37,"volume":301900},{"day":"1998-05-13T00:00:00.000Z","open":34.29,"high":35,"low":34.2,"close":35,"volume":390800},{"day":"1998-05-14T00:00:00.000Z","open":34.92,"high":35,"low":34.35,"close":34.4,"volume":243500},{"day":"1998-05-15T00:00:00.000Z","open":34.55,"high":34.98,"low":34.38,"close":34.71,"volume":285800},{"day":"1998-05-18T00:00:00.000Z","open":34.7,"high":34.95,"low":34.48,"close":34.55,"volume":293400},{"day":"1998-05-19T00:00:00.000Z","open":34.6,"high":34.66,"low":34.31,"close":34.35,"volume":179400},{"day":"1998-05-20T00:00:00.000Z","open":34.4,"high":34.55,"low":34.2,"close":34.4,"volume":216400},{"day":"1998-05-21T00:00:00.000Z","open":34.34,"high":34.4,"low":34,"close":34.2,"volume":194100},{"day":"1998-05-22T00:00:00.000Z","open":34.28,"high":34.46,"low":34.1,"close":34.22,"volume":213500},{"day":"1998-05-25T00:00:00.000Z","open":34.2,"high":34.22,"low":33.8,"close":33.9,"volume":278400},{"day":"1998-05-26T00:00:00.000Z","open":33.9,"high":34.33,"low":33.5,"close":33.9,"volume":212700},{"day":"1998-05-27T00:00:00.000Z","open":33.9,"high":34,"low":33.55,"close":33.79,"volume":239500},{"day":"1998-05-28T00:00:00.000Z","open":33.8,"high":34.2,"low":33.5,"close":33.95,"volume":209300},{"day":"1998-05-29T00:00:00.000Z","open":34,"high":34,"low":33.8,"close":33.93,"volume":246300},{"day":"1998-06-01T00:00:00.000Z","open":33.9,"high":33.9,"low":33.56,"close":33.67,"volume":380100},{"day":"1998-06-02T00:00:00.000Z","open":33.55,"high":34.21,"low":33.12,"close":34.15,"volume":542200},{"day":"1998-06-03T00:00:00.000Z","open":34.1,"high":34.6,"low":33.9,"close":34.28,"volume":422700},{"day":"1998-06-04T00:00:00.000Z","open":34.4,"high":34.45,"low":33.9,"close":34.1,"volume":374600},{"day":"1998-06-05T00:00:00.000Z","open":34,"high":35,"low":33.97,"close":34.7,"volume":563400},{"day":"1998-06-08T00:00:00.000Z","open":34.8,"high":35.85,"low":34.8,"close":35.4,"volume":853800},{"day":"1998-06-09T00:00:00.000Z","open":35.5,"high":35.6,"low":34.5,"close":34.89,"volume":581900},{"day":"1998-06-10T00:00:00.000Z","open":34.6,"high":35.2,"low":34.6,"close":34.85,"volume":321900},{"day":"1998-06-11T00:00:00.000Z","open":34.8,"high":35,"low":34,"close":34,"volume":245800},{"day":"1998-06-12T00:00:00.000Z","open":34.95,"high":34.95,"low":34.2,"close":34.38,"volume":255800},{"day":"1998-06-15T00:00:00.000Z","open":34.48,"high":35,"low":33.9,"close":33.96,"volume":303000},{"day":"1998-06-16T00:00:00.000Z","open":33.8,"high":33.9,"low":33,"close":33.4,"volume":316100},{"day":"1998-06-17T00:00:00.000Z","open":33.45,"high":33.7,"low":32.96,"close":33,"volume":223600},{"day":"1998-06-18T00:00:00.000Z","open":33,"high":33.4,"low":32.93,"close":33.1,"volume":256400},{"day":"1998-06-19T00:00:00.000Z","open":33.3,"high":33.55,"low":33.25,"close":33.5,"volume":196600},{"day":"1998-06-22T00:00:00.000Z","open":33.2,"high":33.98,"low":33.06,"close":33.35,"volume":318300},{"day":"1998-06-23T00:00:00.000Z","open":33,"high":33.35,"low":33,"close":33.2,"volume":236800},{"day":"1998-06-24T00:00:00.000Z","open":33.12,"high":33.98,"low":32,"close":33.91,"volume":569500},{"day":"1998-06-25T00:00:00.000Z","open":33.91,"high":34.05,"low":33.65,"close":33.81,"volume":248200},{"day":"1998-06-26T00:00:00.000Z","open":33.8,"high":33.92,"low":33.5,"close":33.6,"volume":160000},{"day":"1998-06-29T00:00:00.000Z","open":33.5,"high":33.6,"low":32.8,"close":32.85,"volume":255000},{"day":"1998-06-30T00:00:00.000Z","open":33,"high":33.1,"low":31.95,"close":31.98,"volume":214100},{"day":"1998-07-01T00:00:00.000Z","open":32.6,"high":32.99,"low":31.5,"close":31.78,"volume":150900},{"day":"1998-07-02T00:00:00.000Z","open":31.2,"high":32.5,"low":30.5,"close":32.44,"volume":232000},{"day":"1998-07-03T00:00:00.000Z","open":32.4,"high":32.4,"low":31.8,"close":31.96,"volume":135900},{"day":"1998-07-06T00:00:00.000Z","open":31.5,"high":32.05,"low":31.2,"close":31.27,"volume":121600},{"day":"1998-07-07T00:00:00.000Z","open":31,"high":31.6,"low":30.85,"close":31.35,"volume":107200},{"day":"1998-07-08T00:00:00.000Z","open":31.6,"high":32.1,"low":31.4,"close":31.8,"volume":60700},{"day":"1998-07-09T00:00:00.000Z","open":31.5,"high":32.8,"low":31.5,"close":32.55,"volume":166500},{"day":"1998-07-10T00:00:00.000Z","open":32.6,"high":34.4,"low":32.58,"close":34.01,"volume":743700},{"day":"1998-07-13T00:00:00.000Z","open":34.1,"high":34.8,"low":33.8,"close":34.23,"volume":567800},{"day":"1998-07-14T00:00:00.000Z","open":34.1,"high":34.28,"low":33.7,"close":34.11,"volume":390300},{"day":"1998-07-15T00:00:00.000Z","open":34.3,"high":36.7,"low":34,"close":35.79,"volume":2418800},{"day":"1998-07-16T00:00:00.000Z","open":35.81,"high":36.8,"low":35.61,"close":36.26,"volume":1957200},{"day":"1998-07-17T00:00:00.000Z","open":36.25,"high":36.25,"low":35.25,"close":35.8,"volume":1017600},{"day":"1998-07-20T00:00:00.000Z","open":35.6,"high":35.78,"low":34.39,"close":34.98,"volume":877000},{"day":"1998-07-21T00:00:00.000Z","open":34.5,"high":36.36,"low":34.45,"close":35.59,"volume":531300},{"day":"1998-07-22T00:00:00.000Z","open":35.5,"high":36,"low":35,"close":35.35,"volume":440200},{"day":"1998-07-23T00:00:00.000Z","open":35.25,"high":36.5,"low":35.25,"close":36.32,"volume":660500},{"day":"1998-07-24T00:00:00.000Z","open":36.6,"high":37.58,"low":36.22,"close":37.24,"volume":2086400},{"day":"1998-07-27T00:00:00.000Z","open":37.6,"high":37.8,"low":36.8,"close":37.02,"volume":680900},{"day":"1998-07-28T00:00:00.000Z","open":37.04,"high":37.6,"low":36.8,"close":37,"volume":503100},{"day":"1998-07-29T00:00:00.000Z","open":37.51,"high":38.94,"low":37.5,"close":38.9,"volume":3083500},{"day":"1998-07-30T00:00:00.000Z","open":39.8,"high":42.75,"low":38.8,"close":38.95,"volume":6648400},{"day":"1998-07-31T00:00:00.000Z","open":38.97,"high":39.5,"low":38.12,"close":38.28,"volume":2463200},{"day":"1998-08-03T00:00:00.000Z","open":38.4,"high":38.45,"low":37.39,"close":37.41,"volume":1108300},{"day":"1998-08-04T00:00:00.000Z","open":37.35,"high":38.8,"low":36.8,"close":38.59,"volume":1052700},{"day":"1998-08-05T00:00:00.000Z","open":38.58,"high":38.96,"low":38.06,"close":38.58,"volume":628300},{"day":"1998-08-06T00:00:00.000Z","open":38.6,"high":38.88,"low":37.8,"close":37.87,"volume":419500},{"day":"1998-08-07T00:00:00.000Z","open":37.8,"high":38.1,"low":37,"close":37.06,"volume":807400},{"day":"1998-08-10T00:00:00.000Z","open":37.07,"high":37.07,"low":36.1,"close":36.28,"volume":597500},{"day":"1998-08-11T00:00:00.000Z","open":36.28,"high":36.96,"low":36.2,"close":36.29,"volume":400500},{"day":"1998-08-12T00:00:00.000Z","open":36.38,"high":36.88,"low":35.1,"close":36,"volume":579200},{"day":"1998-08-13T00:00:00.000Z","open":36,"high":37.3,"low":36,"close":36.48,"volume":507800},{"day":"1998-08-14T00:00:00.000Z","open":36.5,"high":36.58,"low":35.68,"close":35.71,"volume":420400},{"day":"1998-08-17T00:00:00.000Z","open":35.6,"high":35.6,"low":33,"close":33.09,"volume":760400},{"day":"1998-08-18T00:00:00.000Z","open":33,"high":34.6,"low":32.8,"close":33.7,"volume":733400},{"day":"1998-08-19T00:00:00.000Z","open":33.66,"high":34.9,"low":33.66,"close":34.89,"volume":415600},{"day":"1998-08-20T00:00:00.000Z","open":35.1,"high":35.2,"low":34.3,"close":35.06,"volume":495800},{"day":"1998-08-21T00:00:00.000Z","open":34.8,"high":36.18,"low":34.8,"close":35.94,"volume":593700},{"day":"1998-08-24T00:00:00.000Z","open":36,"high":36.36,"low":35.6,"close":35.86,"volume":634200},{"day":"1998-08-25T00:00:00.000Z","open":35.7,"high":35.9,"low":35.02,"close":35.4,"volume":357500},{"day":"1998-08-26T00:00:00.000Z","open":35.39,"high":36,"low":35.1,"close":35.22,"volume":316700},{"day":"1998-08-27T00:00:00.000Z","open":35.2,"high":35.3,"low":34.8,"close":35.1,"volume":267200},{"day":"1998-08-28T00:00:00.000Z","open":35.03,"high":35.68,"low":34.89,"close":35.18,"volume":335600},{"day":"1998-08-31T00:00:00.000Z","open":34.18,"high":35,"low":32.8,"close":34.82,"volume":178400},{"day":"1998-09-01T00:00:00.000Z","open":35,"high":36.3,"low":34.8,"close":34.8,"volume":291600},{"day":"1998-09-02T00:00:00.000Z","open":34.29,"high":35.1,"low":34.29,"close":34.55,"volume":136100},{"day":"1998-09-03T00:00:00.000Z","open":34.99,"high":35.05,"low":34.56,"close":34.59,"volume":163600},{"day":"1998-09-04T00:00:00.000Z","open":36.5,"high":36.9,"low":35.6,"close":35.96,"volume":781700},{"day":"1998-09-07T00:00:00.000Z","open":36.5,"high":36.9,"low":36.3,"close":36.54,"volume":777900},{"day":"1998-09-08T00:00:00.000Z","open":36.48,"high":36.8,"low":35.87,"close":35.89,"volume":606300},{"day":"1998-09-09T00:00:00.000Z","open":35.9,"high":36.5,"low":35.7,"close":36.49,"volume":682300},{"day":"1998-09-10T00:00:00.000Z","open":36.88,"high":38,"low":36.7,"close":37.49,"volume":2417400},{"day":"1998-09-11T00:00:00.000Z","open":19.2,"high":19.98,"low":19,"close":19.15,"volume":3767100},{"day":"1998-09-14T00:00:00.000Z","open":19.18,"high":19.18,"low":18.49,"close":18.65,"volume":2167000},{"day":"1998-09-15T00:00:00.000Z","open":18.39,"high":18.5,"low":17.84,"close":17.96,"volume":2916200},{"day":"1998-09-16T00:00:00.000Z","open":17.78,"high":18.5,"low":17.7,"close":18.22,"volume":1328200},{"day":"1998-09-17T00:00:00.000Z","open":18.22,"high":18.29,"low":17.73,"close":17.81,"volume":1710400},{"day":"1998-09-18T00:00:00.000Z","open":17.6,"high":17.8,"low":17.4,"close":17.44,"volume":1004700},{"day":"1998-09-21T00:00:00.000Z","open":17,"high":17.65,"low":17,"close":17.57,"volume":818800},{"day":"1998-09-22T00:00:00.000Z","open":17.6,"high":18.2,"low":17.35,"close":17.67,"volume":836200},{"day":"1998-09-23T00:00:00.000Z","open":17.68,"high":17.8,"low":17.05,"close":17.23,"volume":1361000},{"day":"1998-09-24T00:00:00.000Z","open":17.3,"high":17.6,"low":17.04,"close":17.11,"volume":504100},{"day":"1998-09-25T00:00:00.000Z","open":17.15,"high":17.38,"low":16.96,"close":17.17,"volume":561900},{"day":"1998-09-28T00:00:00.000Z","open":17.2,"high":17.2,"low":16.71,"close":16.89,"volume":796100},{"day":"1998-09-29T00:00:00.000Z","open":16.89,"high":17.28,"low":16.72,"close":16.98,"volume":478000},{"day":"1998-09-30T00:00:00.000Z","open":17.3,"high":17.3,"low":16.9,"close":17.14,"volume":505400},{"day":"1998-10-05T00:00:00.000Z","open":17.2,"high":17.22,"low":16.88,"close":16.92,"volume":324900},{"day":"1998-10-06T00:00:00.000Z","open":16.95,"high":17.15,"low":16.9,"close":16.91,"volume":383200},{"day":"1998-10-07T00:00:00.000Z","open":16.94,"high":17,"low":16.7,"close":16.74,"volume":296400},{"day":"1998-10-08T00:00:00.000Z","open":16.75,"high":16.91,"low":16.5,"close":16.79,"volume":457000},{"day":"1998-10-09T00:00:00.000Z","open":16.8,"high":16.85,"low":16.53,"close":16.6,"volume":411200},{"day":"1998-10-12T00:00:00.000Z","open":16.5,"high":16.55,"low":16.1,"close":16.25,"volume":720400},{"day":"1998-10-13T00:00:00.000Z","open":16.2,"high":16.75,"low":16.15,"close":16.28,"volume":374900},{"day":"1998-10-14T00:00:00.000Z","open":16.3,"high":16.6,"low":16.1,"close":16.22,"volume":382300},{"day":"1998-10-15T00:00:00.000Z","open":16.22,"high":16.25,"low":16,"close":16.1,"volume":609500},{"day":"1998-10-16T00:00:00.000Z","open":16.2,"high":16.5,"low":16.1,"close":16.25,"volume":446000},{"day":"1998-10-19T00:00:00.000Z","open":16.3,"high":16.38,"low":16.13,"close":16.22,"volume":521300},{"day":"1998-10-20T00:00:00.000Z","open":16.22,"high":16.31,"low":16.06,"close":16.07,"volume":441300},{"day":"1998-10-21T00:00:00.000Z","open":16.06,"high":16.06,"low":15.6,"close":15.61,"volume":775000},{"day":"1998-10-22T00:00:00.000Z","open":15.58,"high":15.6,"low":15.2,"close":15.39,"volume":590100},{"day":"1998-10-23T00:00:00.000Z","open":15.46,"high":16.79,"low":15.42,"close":16.01,"volume":1116200},{"day":"1998-10-26T00:00:00.000Z","open":15.99,"high":16,"low":15.5,"close":15.7,"volume":516900},{"day":"1998-10-27T00:00:00.000Z","open":15.6,"high":15.6,"low":15.48,"close":15.55,"volume":450600},{"day":"1998-10-28T00:00:00.000Z","open":15.6,"high":15.85,"low":15.5,"close":15.73,"volume":276800},{"day":"1998-10-29T00:00:00.000Z","open":15.9,"high":16.65,"low":15.9,"close":16.32,"volume":959800},{"day":"1998-10-30T00:00:00.000Z","open":16.37,"high":16.75,"low":16.36,"close":16.49,"volume":817500},{"day":"1998-11-02T00:00:00.000Z","open":16.55,"high":16.55,"low":16,"close":16.26,"volume":472000},{"day":"1998-11-03T00:00:00.000Z","open":16.26,"high":16.45,"low":16.2,"close":16.39,"volume":353000},{"day":"1998-11-04T00:00:00.000Z","open":16.5,"high":17.19,"low":16.2,"close":16.89,"volume":1220400},{"day":"1998-11-05T00:00:00.000Z","open":16.89,"high":17.1,"low":16.7,"close":17.07,"volume":818200},{"day":"1998-11-06T00:00:00.000Z","open":17.2,"high":17.9,"low":17.15,"close":17.43,"volume":1850700},{"day":"1998-11-09T00:00:00.000Z","open":17.65,"high":18.7,"low":17.65,"close":18.65,"volume":4090600},{"day":"1998-11-10T00:00:00.000Z","open":18.8,"high":19.1,"low":18.3,"close":18.56,"volume":3699100},{"day":"1998-11-11T00:00:00.000Z","open":18.56,"high":18.71,"low":18.3,"close":18.59,"volume":1363200},{"day":"1998-11-12T00:00:00.000Z","open":18.7,"high":19,"low":18.51,"close":18.63,"volume":1608200},{"day":"1998-11-13T00:00:00.000Z","open":18.65,"high":18.65,"low":18.18,"close":18.31,"volume":921400},{"day":"1998-11-16T00:00:00.000Z","open":18.3,"high":19,"low":18.28,"close":18.77,"volume":1601300},{"day":"1998-11-17T00:00:00.000Z","open":19,"high":19.2,"low":18.6,"close":18.68,"volume":1326100},{"day":"1998-11-18T00:00:00.000Z","open":18.7,"high":18.7,"low":18.28,"close":18.32,"volume":932600},{"day":"1998-11-19T00:00:00.000Z","open":18.25,"high":18.25,"low":17.88,"close":17.95,"volume":883600},{"day":"1998-11-20T00:00:00.000Z","open":17.9,"high":18.08,"low":17.8,"close":17.96,"volume":689400},{"day":"1998-11-23T00:00:00.000Z","open":17.9,"high":17.9,"low":17.6,"close":17.65,"volume":599500},{"day":"1998-11-24T00:00:00.000Z","open":17.8,"high":18.4,"low":17.8,"close":17.95,"volume":585600},{"day":"1998-11-25T00:00:00.000Z","open":17.9,"high":18.3,"low":17.85,"close":17.89,"volume":443800},{"day":"1998-11-26T00:00:00.000Z","open":18,"high":18,"low":17.7,"close":17.75,"volume":369600},{"day":"1998-11-27T00:00:00.000Z","open":17.8,"high":17.95,"low":17.57,"close":17.58,"volume":392300},{"day":"1998-11-30T00:00:00.000Z","open":17.6,"high":17.62,"low":17.25,"close":17.39,"volume":422400},{"day":"1998-12-01T00:00:00.000Z","open":17.4,"high":17.68,"low":17.15,"close":17.21,"volume":344200},{"day":"1998-12-02T00:00:00.000Z","open":17.25,"high":17.29,"low":17.02,"close":17.18,"volume":350600},{"day":"1998-12-03T00:00:00.000Z","open":17.15,"high":17.45,"low":17.1,"close":17.27,"volume":238700},{"day":"1998-12-04T00:00:00.000Z","open":17.21,"high":17.35,"low":17.08,"close":17.13,"volume":245700},{"day":"1998-12-07T00:00:00.000Z","open":17.2,"high":17.61,"low":17,"close":17,"volume":298500},{"day":"1998-12-08T00:00:00.000Z","open":17.01,"high":17.3,"low":17.01,"close":17.09,"volume":215500},{"day":"1998-12-09T00:00:00.000Z","open":17.27,"high":17.27,"low":17.02,"close":17.08,"volume":215400},{"day":"1998-12-10T00:00:00.000Z","open":17.15,"high":17.15,"low":16.8,"close":16.83,"volume":291600},{"day":"1998-12-11T00:00:00.000Z","open":16.78,"high":17.25,"low":16.6,"close":17.18,"volume":327100},{"day":"1998-12-14T00:00:00.000Z","open":17.25,"high":17.37,"low":17.15,"close":17.17,"volume":221700},{"day":"1998-12-15T00:00:00.000Z","open":17.04,"high":17.1,"low":16.81,"close":17,"volume":331300},{"day":"1998-12-16T00:00:00.000Z","open":17,"high":17.25,"low":16.9,"close":16.91,"volume":191100},{"day":"1998-12-17T00:00:00.000Z","open":16.8,"high":16.92,"low":16.57,"close":16.62,"volume":330400},{"day":"1998-12-18T00:00:00.000Z","open":16.59,"high":16.83,"low":16.45,"close":16.53,"volume":316000},{"day":"1998-12-21T00:00:00.000Z","open":16.52,"high":16.7,"low":16.4,"close":16.45,"volume":173000},{"day":"1998-12-22T00:00:00.000Z","open":16.46,"high":17.08,"low":16.46,"close":17.08,"volume":310100},{"day":"1998-12-23T00:00:00.000Z","open":17,"high":17.29,"low":16.9,"close":16.91,"volume":231800},{"day":"1998-12-24T00:00:00.000Z","open":16.91,"high":16.96,"low":16.66,"close":16.7,"volume":211400},{"day":"1998-12-25T00:00:00.000Z","open":16.7,"high":17,"low":16.7,"close":16.74,"volume":148700},{"day":"1998-12-28T00:00:00.000Z","open":16.89,"high":16.94,"low":16.65,"close":16.7,"volume":188300},{"day":"1998-12-29T00:00:00.000Z","open":16.64,"high":16.69,"low":16.48,"close":16.5,"volume":271000},{"day":"1998-12-30T00:00:00.000Z","open":16.6,"high":16.69,"low":16.12,"close":16.44,"volume":407600},{"day":"1998-12-31T00:00:00.000Z","open":16.5,"high":16.8,"low":16.4,"close":16.43,"volume":176400},{"day":"1999-01-04T00:00:00.000Z","open":16.41,"high":16.42,"low":16.09,"close":16.1,"volume":173400},{"day":"1999-01-05T00:00:00.000Z","open":15.99,"high":16,"low":15.5,"close":15.82,"volume":297900},{"day":"1999-01-06T00:00:00.000Z","open":15.95,"high":15.98,"low":15.7,"close":15.91,"volume":246500},{"day":"1999-01-07T00:00:00.000Z","open":16,"high":16.45,"low":15.8,"close":16.08,"volume":257400},{"day":"1999-01-08T00:00:00.000Z","open":16.1,"high":16.48,"low":16.1,"close":16.44,"volume":325600},{"day":"1999-01-11T00:00:00.000Z","open":16.46,"high":16.46,"low":16.2,"close":16.36,"volume":229700},{"day":"1999-01-12T00:00:00.000Z","open":16.36,"high":16.36,"low":16.1,"close":16.18,"volume":302300},{"day":"1999-01-13T00:00:00.000Z","open":16.15,"high":16.28,"low":16.1,"close":16.11,"volume":313600},{"day":"1999-01-14T00:00:00.000Z","open":16.16,"high":16.58,"low":16.13,"close":16.3,"volume":276000},{"day":"1999-01-15T00:00:00.000Z","open":16.38,"high":16.67,"low":16.29,"close":16.64,"volume":388500},{"day":"1999-01-18T00:00:00.000Z","open":16.75,"high":17.86,"low":16.55,"close":17.82,"volume":1514100},{"day":"1999-01-19T00:00:00.000Z","open":18,"high":18.3,"low":17.61,"close":17.7,"volume":1582200},{"day":"1999-01-20T00:00:00.000Z","open":17.7,"high":18.1,"low":17.45,"close":17.46,"volume":626300},{"day":"1999-01-21T00:00:00.000Z","open":17.46,"high":18,"low":17.38,"close":17.9,"volume":613800},{"day":"1999-01-22T00:00:00.000Z","open":18.01,"high":18.05,"low":17.5,"close":17.78,"volume":513100},{"day":"1999-01-25T00:00:00.000Z","open":17.78,"high":17.8,"low":17.14,"close":17.15,"volume":459700},{"day":"1999-01-26T00:00:00.000Z","open":17,"high":17,"low":16.6,"close":16.67,"volume":455300},{"day":"1999-01-27T00:00:00.000Z","open":16.58,"high":16.92,"low":16.55,"close":16.74,"volume":397700},{"day":"1999-01-28T00:00:00.000Z","open":16.74,"high":17.15,"low":16.58,"close":16.7,"volume":234100},{"day":"1999-01-29T00:00:00.000Z","open":16.61,"high":16.8,"low":16.5,"close":16.62,"volume":189900},{"day":"1999-02-01T00:00:00.000Z","open":16.6,"high":16.6,"low":16.2,"close":16.23,"volume":303900},{"day":"1999-02-02T00:00:00.000Z","open":16,"high":16.45,"low":16,"close":16.43,"volume":182400},{"day":"1999-02-03T00:00:00.000Z","open":16.43,"high":16.7,"low":16.43,"close":16.48,"volume":194900},{"day":"1999-02-04T00:00:00.000Z","open":16.41,"high":16.45,"low":16.2,"close":16.25,"volume":223300},{"day":"1999-02-05T00:00:00.000Z","open":16,"high":16.15,"low":15.9,"close":15.98,"volume":447900},{"day":"1999-02-08T00:00:00.000Z","open":15.9,"high":16.15,"low":15.5,"close":15.6,"volume":488800},{"day":"1999-02-09T00:00:00.000Z","open":15.61,"high":16.09,"low":15.51,"close":16.07,"volume":275500},{"day":"1999-03-01T00:00:00.000Z","open":16.09,"high":16.09,"low":15.69,"close":15.99,"volume":193100},{"day":"1999-03-02T00:00:00.000Z","open":16,"high":16.05,"low":15.79,"close":15.87,"volume":118900},{"day":"1999-03-03T00:00:00.000Z","open":15.85,"high":16.4,"low":15.7,"close":16.23,"volume":227400},{"day":"1999-03-04T00:00:00.000Z","open":16.35,"high":16.86,"low":16.34,"close":16.48,"volume":228100},{"day":"1999-03-05T00:00:00.000Z","open":16.55,"high":16.65,"low":16.07,"close":16.22,"volume":181300},{"day":"1999-03-08T00:00:00.000Z","open":16.2,"high":16.26,"low":15.99,"close":16.04,"volume":290000},{"day":"1999-03-09T00:00:00.000Z","open":16.05,"high":16.35,"low":16,"close":16.15,"volume":234900},{"day":"1999-03-10T00:00:00.000Z","open":16.2,"high":16.2,"low":15.96,"close":16.02,"volume":283600},{"day":"1999-03-11T00:00:00.000Z","open":16.3,"high":16.3,"low":16.1,"close":16.2,"volume":314900},{"day":"1999-03-12T00:00:00.000Z","open":16.25,"high":16.25,"low":16.08,"close":16.16,"volume":372800},{"day":"1999-03-15T00:00:00.000Z","open":16.1,"high":16.82,"low":15.97,"close":16.6,"volume":636100},{"day":"1999-03-16T00:00:00.000Z","open":16.61,"high":16.61,"low":16.28,"close":16.35,"volume":226800},{"day":"1999-03-17T00:00:00.000Z","open":16.38,"high":16.48,"low":16.22,"close":16.28,"volume":291800},{"day":"1999-03-18T00:00:00.000Z","open":16.28,"high":16.28,"low":16,"close":16.04,"volume":298800},{"day":"1999-03-19T00:00:00.000Z","open":16.3,"high":16.3,"low":16,"close":16.26,"volume":312300},{"day":"1999-03-22T00:00:00.000Z","open":16.25,"high":16.25,"low":16,"close":16.08,"volume":484600},{"day":"1999-03-23T00:00:00.000Z","open":16,"high":16.15,"low":16,"close":16.11,"volume":304700},{"day":"1999-03-24T00:00:00.000Z","open":16.08,"high":16.47,"low":16.08,"close":16.19,"volume":240900},{"day":"1999-03-25T00:00:00.000Z","open":16.2,"high":16.2,"low":15.98,"close":16.01,"volume":373600},{"day":"1999-03-26T00:00:00.000Z","open":16,"high":16,"low":15.85,"close":15.91,"volume":219300},{"day":"1999-03-29T00:00:00.000Z","open":15.91,"high":15.91,"low":15.68,"close":15.78,"volume":350100},{"day":"1999-03-30T00:00:00.000Z","open":15.75,"high":16.03,"low":15.7,"close":15.76,"volume":179700},{"day":"1999-03-31T00:00:00.000Z","open":15.7,"high":15.76,"low":15.52,"close":15.68,"volume":316900},{"day":"1999-04-01T00:00:00.000Z","open":15.9,"high":15.9,"low":15.6,"close":15.68,"volume":223500},{"day":"1999-04-02T00:00:00.000Z","open":15.68,"high":15.86,"low":15.65,"close":15.75,"volume":280100},{"day":"1999-04-05T00:00:00.000Z","open":15.75,"high":15.75,"low":15.51,"close":15.65,"volume":456100},{"day":"1999-04-06T00:00:00.000Z","open":15.7,"high":16.15,"low":15.6,"close":15.91,"volume":338200},{"day":"1999-04-07T00:00:00.000Z","open":15.98,"high":16,"low":15.65,"close":15.74,"volume":301700},{"day":"1999-04-08T00:00:00.000Z","open":15.78,"high":15.8,"low":15.58,"close":15.68,"volume":528300},{"day":"1999-04-09T00:00:00.000Z","open":15.51,"high":16,"low":15.51,"close":15.7,"volume":303600},{"day":"1999-04-12T00:00:00.000Z","open":15.61,"high":15.62,"low":15.23,"close":15.25,"volume":621600},{"day":"1999-04-13T00:00:00.000Z","open":15.25,"high":15.25,"low":14.89,"close":14.95,"volume":592100},{"day":"1999-04-14T00:00:00.000Z","open":14.95,"high":14.96,"low":14.79,"close":14.85,"volume":568800},{"day":"1999-04-15T00:00:00.000Z","open":14.9,"high":15.21,"low":14.86,"close":14.91,"volume":289000},{"day":"1999-04-16T00:00:00.000Z","open":15,"high":15.03,"low":14.88,"close":14.94,"volume":286600},{"day":"1999-04-19T00:00:00.000Z","open":14.95,"high":15,"low":14.5,"close":14.55,"volume":411300},{"day":"1999-04-20T00:00:00.000Z","open":14.7,"high":14.79,"low":14.55,"close":14.6,"volume":239700},{"day":"1999-04-21T00:00:00.000Z","open":14.7,"high":14.95,"low":14.4,"close":14.43,"volume":245700},{"day":"1999-04-22T00:00:00.000Z","open":14.38,"high":14.65,"low":13.66,"close":14.46,"volume":617900},{"day":"1999-04-23T00:00:00.000Z","open":14.45,"high":14.48,"low":14,"close":14.2,"volume":344700},{"day":"1999-04-26T00:00:00.000Z","open":13.95,"high":13.97,"low":13.36,"close":13.36,"volume":751500},{"day":"1999-04-27T00:00:00.000Z","open":13.02,"high":13.25,"low":12.6,"close":12.9,"volume":667600},{"day":"1999-04-28T00:00:00.000Z","open":12.92,"high":13.45,"low":12.9,"close":13.05,"volume":350000},{"day":"1999-04-29T00:00:00.000Z","open":13,"high":13.25,"low":12.98,"close":13.24,"volume":280000},{"day":"1999-04-30T00:00:00.000Z","open":13.3,"high":13.55,"low":13.25,"close":13.42,"volume":227700},{"day":"1999-05-04T00:00:00.000Z","open":13.35,"high":13.35,"low":13.05,"close":13.06,"volume":217000},{"day":"1999-05-05T00:00:00.000Z","open":13,"high":13.12,"low":12.9,"close":13.1,"volume":232600},{"day":"1999-05-06T00:00:00.000Z","open":13.1,"high":13.34,"low":13,"close":13.2,"volume":169400},{"day":"1999-05-07T00:00:00.000Z","open":13.16,"high":13.3,"low":13.16,"close":13.2,"volume":166300},{"day":"1999-05-10T00:00:00.000Z","open":13,"high":13,"low":12.2,"close":12.36,"volume":511800},{"day":"1999-05-11T00:00:00.000Z","open":12.36,"high":12.7,"low":12.3,"close":12.5,"volume":244600},{"day":"1999-05-12T00:00:00.000Z","open":12.53,"high":12.7,"low":12.4,"close":12.48,"volume":213100},{"day":"1999-05-13T00:00:00.000Z","open":12.49,"high":12.5,"low":12.3,"close":12.34,"volume":227800},{"day":"1999-05-14T00:00:00.000Z","open":12.35,"high":12.4,"low":11.95,"close":11.96,"volume":375900},{"day":"1999-05-17T00:00:00.000Z","open":11.9,"high":11.9,"low":11.4,"close":11.6,"volume":352300},{"day":"1999-05-18T00:00:00.000Z","open":11.5,"high":11.77,"low":11.45,"close":11.46,"volume":222800},{"day":"1999-05-19T00:00:00.000Z","open":11.5,"high":12.35,"low":11.45,"close":12.31,"volume":608100},{"day":"1999-05-20T00:00:00.000Z","open":12.31,"high":13.12,"low":12.31,"close":12.91,"volume":977100},{"day":"1999-05-21T00:00:00.000Z","open":13,"high":13.8,"low":13,"close":13.53,"volume":1279200},{"day":"1999-05-24T00:00:00.000Z","open":13.8,"high":14.6,"low":13.55,"close":14.59,"volume":2218200},{"day":"1999-05-25T00:00:00.000Z","open":14.78,"high":15.26,"low":14.5,"close":14.53,"volume":2988800},{"day":"1999-05-27T00:00:00.000Z","open":15.98,"high":15.98,"low":15.98,"close":15.98,"volume":458700},{"day":"1999-05-28T00:00:00.000Z","open":17.2,"high":17.48,"low":16.1,"close":16.98,"volume":4892200},{"day":"1999-05-31T00:00:00.000Z","open":17,"high":17.2,"low":15.6,"close":16.09,"volume":2320400},{"day":"1999-06-01T00:00:00.000Z","open":16.07,"high":16.07,"low":15,"close":15.93,"volume":1484800},{"day":"1999-06-02T00:00:00.000Z","open":15.93,"high":16.38,"low":15.4,"close":15.42,"volume":1555200},{"day":"1999-06-03T00:00:00.000Z","open":15.3,"high":15.3,"low":14.8,"close":14.83,"volume":1161900},{"day":"1999-06-04T00:00:00.000Z","open":14.83,"high":15.2,"low":14.8,"close":15.19,"volume":1121500},{"day":"1999-06-07T00:00:00.000Z","open":15.31,"high":16.05,"low":15.3,"close":15.84,"volume":1335400},{"day":"1999-06-08T00:00:00.000Z","open":16,"high":16.1,"low":15.58,"close":15.62,"volume":898800},{"day":"1999-06-09T00:00:00.000Z","open":15.65,"high":15.88,"low":15.25,"close":15.7,"volume":785600},{"day":"1999-06-10T00:00:00.000Z","open":16.3,"high":16.3,"low":15.7,"close":15.82,"volume":1714500},{"day":"1999-06-11T00:00:00.000Z","open":15.75,"high":16.06,"low":15.56,"close":15.78,"volume":859600},{"day":"1999-06-14T00:00:00.000Z","open":15.85,"high":16.5,"low":15.76,"close":16.39,"volume":2115600},{"day":"1999-06-15T00:00:00.000Z","open":17,"high":17.38,"low":15.4,"close":15.61,"volume":1646900},{"day":"1999-06-16T00:00:00.000Z","open":15.61,"high":16.54,"low":15.61,"close":16.5,"volume":1740700},{"day":"1999-06-17T00:00:00.000Z","open":16.8,"high":18.09,"low":16.8,"close":18,"volume":6778200},{"day":"1999-06-18T00:00:00.000Z","open":18.1,"high":18.3,"low":17.58,"close":17.64,"volume":4708000},{"day":"1999-06-21T00:00:00.000Z","open":17.25,"high":17.8,"low":17.1,"close":17.45,"volume":2821800},{"day":"1999-06-22T00:00:00.000Z","open":17.6,"high":18.5,"low":17.34,"close":18.48,"volume":3519300},{"day":"1999-06-23T00:00:00.000Z","open":18.5,"high":18.78,"low":18,"close":18.47,"volume":2981000},{"day":"1999-06-24T00:00:00.000Z","open":18.5,"high":19.4,"low":18.35,"close":19.34,"volume":6120400},{"day":"1999-06-25T00:00:00.000Z","open":19.5,"high":21.27,"low":19.48,"close":19.91,"volume":12146500},{"day":"1999-06-28T00:00:00.000Z","open":20.3,"high":21.9,"low":20.25,"close":21.9,"volume":10038600},{"day":"1999-06-29T00:00:00.000Z","open":22.9,"high":24.09,"low":22.8,"close":24.09,"volume":10567000},{"day":"1999-06-30T00:00:00.000Z","open":24.5,"high":24.5,"low":21.75,"close":22.61,"volume":5234100},{"day":"1999-07-01T00:00:00.000Z","open":22.1,"high":22.31,"low":20.35,"close":20.35,"volume":5337800},{"day":"1999-07-02T00:00:00.000Z","open":20,"high":21,"low":18.61,"close":20.89,"volume":7030300},{"day":"1999-07-05T00:00:00.000Z","open":21,"high":21.3,"low":19.5,"close":19.61,"volume":3787000},{"day":"1999-07-06T00:00:00.000Z","open":19.2,"high":19.5,"low":17.95,"close":18.17,"volume":3722800},{"day":"1999-07-07T00:00:00.000Z","open":19.3,"high":19.3,"low":18.2,"close":19.29,"volume":3036200},{"day":"1999-07-08T00:00:00.000Z","open":19.6,"high":20,"low":19.29,"close":19.71,"volume":2407800},{"day":"1999-07-09T00:00:00.000Z","open":19.7,"high":20.1,"low":19.2,"close":19.66,"volume":1824400},{"day":"1999-07-12T00:00:00.000Z","open":19.7,"high":20.86,"low":19.3,"close":20.33,"volume":2920200},{"day":"1999-07-13T00:00:00.000Z","open":20.3,"high":20.3,"low":19.15,"close":19.35,"volume":1920900},{"day":"1999-07-14T00:00:00.000Z","open":19.24,"high":21.2,"low":19.1,"close":20.79,"volume":2617500},{"day":"1999-07-15T00:00:00.000Z","open":20.8,"high":20.9,"low":19.9,"close":19.94,"volume":2155100},{"day":"1999-07-16T00:00:00.000Z","open":19.8,"high":19.8,"low":19,"close":19.25,"volume":1247800},{"day":"1999-07-19T00:00:00.000Z","open":18.55,"high":19,"low":18,"close":18.06,"volume":1877180},{"day":"1999-07-20T00:00:00.000Z","open":17.9,"high":19.8,"low":17.71,"close":19.79,"volume":2599100},{"day":"1999-07-21T00:00:00.000Z","open":20,"high":20.6,"low":19.55,"close":20.25,"volume":4675400},{"day":"1999-07-22T00:00:00.000Z","open":21.9,"high":22,"low":20.3,"close":20.57,"volume":9238200},{"day":"1999-07-23T00:00:00.000Z","open":20.6,"high":21.32,"low":20.58,"close":20.72,"volume":3815000},{"day":"1999-07-26T00:00:00.000Z","open":20.8,"high":21,"low":20.25,"close":20.3,"volume":1708600},{"day":"1999-07-27T00:00:00.000Z","open":20.45,"high":20.5,"low":19.9,"close":20.1,"volume":1377300},{"day":"1999-07-28T00:00:00.000Z","open":20.11,"high":20.6,"low":20,"close":20.21,"volume":965200},{"day":"1999-07-29T00:00:00.000Z","open":20.3,"high":20.61,"low":19.95,"close":20.1,"volume":784700},{"day":"1999-07-30T00:00:00.000Z","open":20.03,"high":20.28,"low":19.8,"close":20,"volume":1027900},{"day":"1999-08-02T00:00:00.000Z","open":20,"high":20.42,"low":19.81,"close":20.08,"volume":1029900},{"day":"1999-08-03T00:00:00.000Z","open":20.15,"high":20.3,"low":20.02,"close":20.06,"volume":890100},{"day":"1999-08-04T00:00:00.000Z","open":20.15,"high":20.6,"low":20.08,"close":20.46,"volume":1142700},{"day":"1999-08-05T00:00:00.000Z","open":20.5,"high":21.1,"low":20.4,"close":20.86,"volume":1748800},{"day":"1999-08-06T00:00:00.000Z","open":20.9,"high":20.9,"low":19.81,"close":20.36,"volume":1471000},{"day":"1999-08-09T00:00:00.000Z","open":20.4,"high":20.68,"low":19.81,"close":19.89,"volume":981300},{"day":"1999-08-10T00:00:00.000Z","open":19.85,"high":19.85,"low":19.38,"close":19.41,"volume":920400},{"day":"1999-08-11T00:00:00.000Z","open":19.2,"high":19.6,"low":18.9,"close":19.01,"volume":858300},{"day":"1999-08-12T00:00:00.000Z","open":18.8,"high":19.2,"low":18.8,"close":18.86,"volume":765700},{"day":"1999-08-13T00:00:00.000Z","open":19,"high":19.18,"low":18.8,"close":19,"volume":562200},{"day":"1999-08-17T00:00:00.000Z","open":19.8,"high":19.95,"low":19,"close":19.13,"volume":462000},{"day":"1999-08-18T00:00:00.000Z","open":19.2,"high":19.35,"low":18.5,"close":18.91,"volume":560600},{"day":"1999-08-19T00:00:00.000Z","open":18.96,"high":19.95,"low":18.96,"close":19.76,"volume":1130700},{"day":"1999-08-20T00:00:00.000Z","open":20.2,"high":20.25,"low":19.38,"close":19.4,"volume":679500},{"day":"1999-08-23T00:00:00.000Z","open":19.4,"high":19.4,"low":18.88,"close":19.14,"volume":657100},{"day":"1999-08-24T00:00:00.000Z","open":19.4,"high":19.4,"low":19,"close":19.05,"volume":756200},{"day":"1999-08-25T00:00:00.000Z","open":19.05,"high":19.1,"low":18.8,"close":18.88,"volume":670400},{"day":"1999-08-26T00:00:00.000Z","open":18.8,"high":19.7,"low":18.7,"close":19.2,"volume":464100},{"day":"1999-08-27T00:00:00.000Z","open":19.46,"high":19.46,"low":19,"close":19.02,"volume":313900},{"day":"1999-08-30T00:00:00.000Z","open":19,"high":19,"low":18.8,"close":18.85,"volume":562200},{"day":"1999-08-31T00:00:00.000Z","open":19,"high":19.06,"low":18.84,"close":18.93,"volume":340300},{"day":"1999-09-01T00:00:00.000Z","open":19.08,"high":19.1,"low":18.51,"close":18.6,"volume":430900},{"day":"1999-09-02T00:00:00.000Z","open":18.51,"high":18.66,"low":18.08,"close":18.63,"volume":554900},{"day":"1999-09-03T00:00:00.000Z","open":18.6,"high":18.62,"low":18.18,"close":18.23,"volume":393200},{"day":"1999-09-06T00:00:00.000Z","open":18,"high":18,"low":17.6,"close":17.68,"volume":593700},{"day":"1999-09-07T00:00:00.000Z","open":17.65,"high":18,"low":17.6,"close":17.68,"volume":401800},{"day":"1999-09-08T00:00:00.000Z","open":17.68,"high":17.97,"low":17.55,"close":17.9,"volume":220700},{"day":"1999-09-09T00:00:00.000Z","open":18.15,"high":19.39,"low":18.15,"close":19.37,"volume":1103500},{"day":"1999-09-10T00:00:00.000Z","open":19.51,"high":19.8,"low":19,"close":19.01,"volume":1088600},{"day":"1999-09-13T00:00:00.000Z","open":18.99,"high":18.99,"low":18.65,"close":18.75,"volume":622600},{"day":"1999-09-14T00:00:00.000Z","open":18.8,"high":19.05,"low":18.68,"close":18.73,"volume":477700},{"day":"1999-09-15T00:00:00.000Z","open":18.82,"high":18.83,"low":18.4,"close":18.45,"volume":442400},{"day":"1999-09-16T00:00:00.000Z","open":18.5,"high":18.5,"low":18.16,"close":18.25,"volume":359400},{"day":"1999-09-17T00:00:00.000Z","open":18.15,"high":18.43,"low":18.15,"close":18.4,"volume":341600},{"day":"1999-09-20T00:00:00.000Z","open":18.18,"high":18.25,"low":18.18,"close":18.21,"volume":276000},{"day":"1999-09-21T00:00:00.000Z","open":18.19,"high":18.21,"low":18.03,"close":18.05,"volume":250800},{"day":"1999-09-22T00:00:00.000Z","open":18.05,"high":18.3,"low":18.05,"close":18.06,"volume":207500},{"day":"1999-09-23T00:00:00.000Z","open":18.05,"high":18.2,"low":17.9,"close":17.92,"volume":334300},{"day":"1999-09-24T00:00:00.000Z","open":17.93,"high":18.49,"low":17.93,"close":18.1,"volume":147700},{"day":"1999-09-27T00:00:00.000Z","open":18.2,"high":18.48,"low":17.9,"close":17.92,"volume":202700},{"day":"1999-09-28T00:00:00.000Z","open":17.95,"high":18,"low":17.5,"close":17.75,"volume":316200},{"day":"1999-09-29T00:00:00.000Z","open":17.78,"high":18,"low":17.69,"close":17.71,"volume":274830},{"day":"1999-09-30T00:00:00.000Z","open":17.8,"high":17.83,"low":17.6,"close":17.6,"volume":316300},{"day":"1999-10-08T00:00:00.000Z","open":17.6,"high":17.7,"low":17.18,"close":17.2,"volume":216200},{"day":"1999-10-11T00:00:00.000Z","open":17.2,"high":17.2,"low":16.8,"close":16.92,"volume":279600},{"day":"1999-10-12T00:00:00.000Z","open":16.85,"high":17.1,"low":16.8,"close":16.87,"volume":198800},{"day":"1999-10-13T00:00:00.000Z","open":16.9,"high":17,"low":16.81,"close":16.88,"volume":214100},{"day":"1999-10-14T00:00:00.000Z","open":16.75,"high":17.2,"low":16.7,"close":16.87,"volume":161700},{"day":"1999-10-15T00:00:00.000Z","open":17,"high":17.2,"low":16.88,"close":17.14,"volume":299500},{"day":"1999-10-18T00:00:00.000Z","open":17.35,"high":17.4,"low":16.75,"close":16.75,"volume":155400},{"day":"1999-10-19T00:00:00.000Z","open":16.7,"high":16.8,"low":16.1,"close":16.1,"volume":313000},{"day":"1999-10-20T00:00:00.000Z","open":16.15,"high":16.35,"low":15.9,"close":16.12,"volume":243100},{"day":"1999-10-21T00:00:00.000Z","open":16.2,"high":16.2,"low":15.62,"close":15.65,"volume":415700},{"day":"1999-10-22T00:00:00.000Z","open":15.6,"high":16.44,"low":15.55,"close":16.04,"volume":400000},{"day":"1999-10-25T00:00:00.000Z","open":16,"high":16.1,"low":15.81,"close":15.86,"volume":242600},{"day":"1999-10-26T00:00:00.000Z","open":15.9,"high":16.16,"low":15.9,"close":15.99,"volume":216200},{"day":"1999-10-27T00:00:00.000Z","open":16.5,"high":16.8,"low":16.5,"close":16.59,"volume":422700},{"day":"1999-10-28T00:00:00.000Z","open":16.8,"high":16.89,"low":16.16,"close":16.17,"volume":260400},{"day":"1999-10-29T00:00:00.000Z","open":16.32,"high":16.32,"low":15.98,"close":16.04,"volume":198000},{"day":"1999-11-01T00:00:00.000Z","open":16,"high":16.1,"low":15.85,"close":15.9,"volume":197200},{"day":"1999-11-02T00:00:00.000Z","open":16,"high":16.09,"low":15.81,"close":15.9,"volume":104400},{"day":"1999-11-03T00:00:00.000Z","open":15.93,"high":15.93,"low":15.8,"close":15.8,"volume":188600},{"day":"1999-11-04T00:00:00.000Z","open":15.85,"high":15.85,"low":15.6,"close":15.75,"volume":173600},{"day":"1999-11-05T00:00:00.000Z","open":15.8,"high":16,"low":15.68,"close":15.73,"volume":112200},{"day":"1999-11-08T00:00:00.000Z","open":15.78,"high":15.9,"low":15.75,"close":15.8,"volume":204400},{"day":"1999-11-09T00:00:00.000Z","open":15.9,"high":15.95,"low":15.68,"close":15.69,"volume":196200},{"day":"1999-11-10T00:00:00.000Z","open":15.72,"high":15.8,"low":15.3,"close":15.37,"volume":252500},{"day":"1999-11-11T00:00:00.000Z","open":15.42,"high":15.43,"low":15.21,"close":15.25,"volume":201500},{"day":"1999-11-12T00:00:00.000Z","open":15.25,"high":15.38,"low":15.21,"close":15.25,"volume":125200},{"day":"1999-11-15T00:00:00.000Z","open":15.26,"high":15.28,"low":15,"close":15.17,"volume":162900},{"day":"1999-11-16T00:00:00.000Z","open":15.3,"high":15.6,"low":15.02,"close":15.02,"volume":185700},{"day":"1999-11-17T00:00:00.000Z","open":15.1,"high":15.24,"low":14.88,"close":15.15,"volume":261500},{"day":"1999-11-18T00:00:00.000Z","open":15.15,"high":16,"low":15.15,"close":15.76,"volume":438600},{"day":"1999-11-19T00:00:00.000Z","open":15.85,"high":15.95,"low":15.5,"close":15.5,"volume":197300},{"day":"1999-11-22T00:00:00.000Z","open":15.5,"high":15.69,"low":15.5,"close":15.68,"volume":225000},{"day":"1999-11-23T00:00:00.000Z","open":15.7,"high":15.8,"low":15.54,"close":15.63,"volume":145000},{"day":"1999-11-24T00:00:00.000Z","open":15.65,"high":15.7,"low":15.38,"close":15.45,"volume":143500},{"day":"1999-11-25T00:00:00.000Z","open":15.4,"high":15.45,"low":15.23,"close":15.26,"volume":93000},{"day":"1999-11-26T00:00:00.000Z","open":15.26,"high":15.35,"low":15.19,"close":15.2,"volume":106400},{"day":"1999-11-29T00:00:00.000Z","open":15.15,"high":15.28,"low":15.1,"close":15.14,"volume":119600},{"day":"1999-11-30T00:00:00.000Z","open":15.2,"high":15.25,"low":15.1,"close":15.18,"volume":110100},{"day":"1999-12-01T00:00:00.000Z","open":15.2,"high":15.5,"low":15.18,"close":15.35,"volume":103900},{"day":"1999-12-02T00:00:00.000Z","open":15.35,"high":15.45,"low":15.3,"close":15.31,"volume":91300},{"day":"1999-12-03T00:00:00.000Z","open":15.3,"high":15.31,"low":15.2,"close":15.22,"volume":120900},{"day":"1999-12-06T00:00:00.000Z","open":15.2,"high":15.23,"low":15.05,"close":15.11,"volume":156400},{"day":"1999-12-07T00:00:00.000Z","open":15.05,"high":15.5,"low":15,"close":15.1,"volume":215100},{"day":"1999-12-08T00:00:00.000Z","open":15.15,"high":15.16,"low":15.04,"close":15.1,"volume":115400},{"day":"1999-12-09T00:00:00.000Z","open":15.1,"high":15.1,"low":15.03,"close":15.03,"volume":152300},{"day":"1999-12-10T00:00:00.000Z","open":15,"high":15.28,"low":15,"close":15.25,"volume":142010},{"day":"1999-12-13T00:00:00.000Z","open":15.23,"high":15.3,"low":15.1,"close":15.14,"volume":154700},{"day":"1999-12-14T00:00:00.000Z","open":15.27,"high":15.93,"low":15.27,"close":15.79,"volume":702900},{"day":"1999-12-15T00:00:00.000Z","open":15.8,"high":16.2,"low":15.8,"close":15.95,"volume":757300},{"day":"1999-12-16T00:00:00.000Z","open":15.95,"high":16,"low":15.55,"close":15.64,"volume":279900},{"day":"1999-12-17T00:00:00.000Z","open":15.6,"high":15.6,"low":15.22,"close":15.25,"volume":242500},{"day":"1999-12-21T00:00:00.000Z","open":15.11,"high":15.24,"low":15,"close":15.1,"volume":245500},{"day":"1999-12-22T00:00:00.000Z","open":15.2,"high":15.2,"low":15,"close":15.06,"volume":169600},{"day":"1999-12-23T00:00:00.000Z","open":15.18,"high":15.18,"low":14.65,"close":14.75,"volume":264100},{"day":"1999-12-24T00:00:00.000Z","open":14.75,"high":14.75,"low":14.45,"close":14.5,"volume":197900},{"day":"1999-12-27T00:00:00.000Z","open":14.5,"high":14.75,"low":14.48,"close":14.6,"volume":178800},{"day":"1999-12-28T00:00:00.000Z","open":14.7,"high":14.9,"low":14.41,"close":14.45,"volume":143700},{"day":"1999-12-29T00:00:00.000Z","open":14.88,"high":14.88,"low":14.47,"close":14.62,"volume":136900},{"day":"1999-12-30T00:00:00.000Z","open":14.7,"high":14.85,"low":14.68,"close":14.73,"volume":113300},{"day":"2000-01-04T00:00:00.000Z","open":14.8,"high":15.15,"low":14.65,"close":15.08,"volume":355000},{"day":"2000-01-05T00:00:00.000Z","open":15.08,"high":15.3,"low":14.88,"close":14.89,"volume":436600},{"day":"2000-01-06T00:0
Download .txt
gitextract_bedscd2c/

├── .gitignore
├── README.md
├── app/
│   ├── .gitignore
│   ├── build.gradle
│   ├── proguard-rules.pro
│   └── src/
│       ├── androidTest/
│       │   └── java/
│       │       └── com/
│       │           └── guannan/
│       │               └── stockchart/
│       │                   └── ExampleInstrumentedTest.java
│       ├── main/
│       │   ├── AndroidManifest.xml
│       │   ├── java/
│       │   │   └── com/
│       │   │       └── guannan/
│       │   │           └── stockchart/
│       │   │               └── MainActivity.java
│       │   └── res/
│       │       ├── drawable/
│       │       │   ├── ic_launcher_background.xml
│       │       │   └── selector_line_bg.xml
│       │       ├── drawable-v24/
│       │       │   └── ic_launcher_foreground.xml
│       │       ├── layout/
│       │       │   ├── activity_main.xml
│       │       │   └── pankou_layout.xml
│       │       ├── mipmap-anydpi-v26/
│       │       │   ├── ic_launcher.xml
│       │       │   └── ic_launcher_round.xml
│       │       └── values/
│       │           ├── colors.xml
│       │           ├── strings.xml
│       │           └── styles.xml
│       └── test/
│           └── java/
│               └── com/
│                   └── guannan/
│                       └── stockchart/
│                           └── ExampleUnitTest.java
├── build.gradle
├── chartmodule/
│   ├── .gitignore
│   ├── build.gradle
│   ├── consumer-rules.pro
│   ├── proguard-rules.pro
│   └── src/
│       └── main/
│           ├── AndroidManifest.xml
│           ├── java/
│           │   └── com/
│           │       └── guannan/
│           │           └── chartmodule/
│           │               ├── chart/
│           │               │   ├── BaseChartView.java
│           │               │   ├── KMasterChartView.java
│           │               │   ├── KSubChartView.java
│           │               │   ├── MarketFigureChart.java
│           │               │   └── ViewPortHandler.java
│           │               ├── data/
│           │               │   ├── ExtremeValue.java
│           │               │   ├── KLineToDrawItem.java
│           │               │   ├── LineRectItem.java
│           │               │   ├── SubChartData.java
│           │               │   └── TechItem.java
│           │               ├── helper/
│           │               │   ├── ChartDataSourceHelper.java
│           │               │   ├── ChartTouchHelper.java
│           │               │   ├── TechParamType.java
│           │               │   └── TechParamsHelper.java
│           │               ├── inter/
│           │               │   ├── IChartDataCountListener.java
│           │               │   ├── IChartGestureListener.java
│           │               │   ├── IPressChangeListener.java
│           │               │   └── ITouchResponseListener.java
│           │               └── utils/
│           │                   ├── DataUtils.java
│           │                   ├── DateUtils.java
│           │                   ├── DisplayUtils.java
│           │                   ├── LogUtils.java
│           │                   ├── NumFormatUtils.java
│           │                   ├── PaintUtils.java
│           │                   └── StringUtils.java
│           └── res/
│               └── values/
│                   ├── colors.xml
│                   └── strings.xml
├── gradle/
│   └── wrapper/
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
└── simulateddata/
    ├── .gitignore
    ├── build.gradle
    ├── consumer-rules.pro
    ├── libs/
    │   └── json-smart-2.1.1.jar
    ├── proguard-rules.pro
    └── src/
        └── main/
            ├── AndroidManifest.xml
            ├── assets/
            │   ├── geli.json
            │   ├── maotai.json
            │   ├── pingan.json
            │   └── slw_k.json
            ├── java/
            │   └── com/
            │       └── guannan/
            │           └── simulateddata/
            │               ├── LocalUtils.java
            │               ├── entity/
            │               │   └── KLineItem.java
            │               └── parser/
            │                   └── KLineParser.java
            └── res/
                └── values/
                    └── strings.xml
Download .txt
SYMBOL INDEX (194 symbols across 31 files)

FILE: app/src/androidTest/java/com/guannan/stockchart/ExampleInstrumentedTest.java
  class ExampleInstrumentedTest (line 17) | @RunWith(AndroidJUnit4.class)
    method useAppContext (line 19) | @Test

FILE: app/src/main/java/com/guannan/stockchart/MainActivity.java
  class MainActivity (line 24) | public class MainActivity extends AppCompatActivity
    method onCreate (line 38) | @Override
    method initialData (line 64) | private void initialData(final String json) {
    method initViews (line 74) | private void initViews() {
    method onCheckedChanged (line 82) | @Override
    method initData (line 103) | public void initData(String json) {
    method onReady (line 120) | @Override
    method onChartTranslate (line 131) | @Override
    method onChartFling (line 141) | @Override
    method onChartScale (line 148) | @Override

FILE: app/src/test/java/com/guannan/stockchart/ExampleUnitTest.java
  class ExampleUnitTest (line 12) | public class ExampleUnitTest {
    method addition_isCorrect (line 13) | @Test

FILE: chartmodule/src/main/java/com/guannan/chartmodule/chart/BaseChartView.java
  class BaseChartView (line 30) | public abstract class BaseChartView extends View implements ITouchRespon...
    method onAttachedToWindow (line 105) | @Override
    method BaseChartView (line 111) | public BaseChartView(Context context) {
    method BaseChartView (line 115) | public BaseChartView(Context context,
    method BaseChartView (line 120) | public BaseChartView(Context context, @Nullable AttributeSet attrs, in...
    method initRunnable (line 141) | private void initRunnable() {
    method initHandler (line 150) | private void initHandler() {
    method onChartLongPressed (line 154) | public abstract void onChartLongPressed(MotionEvent me);
    method onChartGestureEnd (line 156) | public abstract void onChartGestureEnd(MotionEvent me,
    method onChartSingleTapped (line 159) | public abstract void onChartSingleTapped(MotionEvent me);
    class ChartHandler (line 164) | private static final class ChartHandler extends Handler {
      method ChartHandler (line 168) | public ChartHandler(BaseChartView view) {
      method handleMessage (line 172) | @Override
    method onSizeChanged (line 203) | @Override
    method initCanvas (line 215) | private void initCanvas() {
    method createBitmap (line 235) | private Bitmap createBitmap(int width, int height, Bitmap.Config confi...
    method onDetachedFromWindow (line 250) | @Override
    class DoubleBuffering (line 284) | class DoubleBuffering implements Runnable {
      method DoubleBuffering (line 288) | public DoubleBuffering(BaseChartView view) {
      method run (line 292) | @Override
    method drawFrame (line 311) | protected void drawFrame(Canvas canvas) {
    method invalidateView (line 318) | public void invalidateView() {
    method getString (line 323) | public String getString(int id) {
    method postInvalidateView (line 330) | public void postInvalidateView() {
    method onDraw (line 335) | @Override

FILE: chartmodule/src/main/java/com/guannan/chartmodule/chart/KMasterChartView.java
  class KMasterChartView (line 32) | public class KMasterChartView extends BaseChartView {
    method KMasterChartView (line 76) | public KMasterChartView(Context context) {
    method KMasterChartView (line 80) | public KMasterChartView(Context context,
    method KMasterChartView (line 85) | public KMasterChartView(Context context, @Nullable AttributeSet attrs,...
    method onSizeChanged (line 94) | @Override
    method getViewPortHandler (line 102) | public ViewPortHandler getViewPortHandler() {
    method drawFrame (line 106) | @Override
    method drawBollDes (line 167) | private void drawBollDes(Canvas canvas, RectF contentRect, KLineToDraw...
    method showLongPressDialog (line 193) | private void showLongPressDialog(Canvas canvas, RectF contentRect) {
    method drawPopText (line 229) | public void drawPopText(Canvas canvas, RectF popRect, String title, St...
    method drawOutLine (line 255) | private void drawOutLine(Canvas canvas) {
    method initData (line 297) | public void initData(List<KLineToDrawItem> klineList, ExtremeValue ext...
    method onChartLongPressed (line 308) | @Override
    method onChartGestureEnd (line 327) | @Override
    method onChartSingleTapped (line 339) | @Override

FILE: chartmodule/src/main/java/com/guannan/chartmodule/chart/KSubChartView.java
  class KSubChartView (line 31) | public class KSubChartView extends BaseChartView {
    method KSubChartView (line 70) | public KSubChartView(Context context) {
    method KSubChartView (line 74) | public KSubChartView(Context context, @Nullable AttributeSet attrs) {
    method KSubChartView (line 78) | public KSubChartView(Context context, @Nullable AttributeSet attrs, in...
    method onSizeChanged (line 84) | @Override
    method getViewPortHandler (line 92) | public ViewPortHandler getViewPortHandler() {
    method drawFrame (line 96) | @Override
    method drawVolumeDes (line 145) | private void drawVolumeDes(Canvas canvas, RectF contentRect, KLineToDr...
    method drawTechDes (line 155) | public void drawTechDes(Canvas canvas, RectF contentRect, KLineToDrawI...
    method drawMacd (line 182) | private void drawMacd(Canvas canvas, RectF contentRect) {
    method drawVolume (line 206) | private void drawVolume(Canvas canvas, RectF contentRect) {
    method drawOutLine (line 228) | private void drawOutLine(Canvas canvas) {
    method initData (line 255) | public void initData(List<KLineToDrawItem> data, ExtremeValue extremeV...
    method onChartLongPressed (line 267) | @Override
    method onChartGestureEnd (line 286) | @Override
    method onChartSingleTapped (line 298) | @Override

FILE: chartmodule/src/main/java/com/guannan/chartmodule/chart/MarketFigureChart.java
  class MarketFigureChart (line 21) | public class MarketFigureChart extends LinearLayout implements IChartGes...
    method MarketFigureChart (line 27) | public MarketFigureChart(Context context) {
    method MarketFigureChart (line 31) | public MarketFigureChart(Context context,
    method MarketFigureChart (line 36) | public MarketFigureChart(Context context, @Nullable AttributeSet attrs...
    method addChildChart (line 50) | public void addChildChart(BaseChartView childView, float height) {
    method setPressChangeListener (line 57) | public void setPressChangeListener(IPressChangeListener listener) {
    method onChartGestureStart (line 61) | @Override
    method onChartGestureEnd (line 67) | @Override
    method onChartLongPressed (line 78) | @Override
    method onChartDoubleTapped (line 88) | @Override
    method onChartSingleTapped (line 93) | @Override
    method onChartFling (line 103) | @Override
    method onChartScale (line 110) | @Override
    method onChartTranslate (line 117) | @Override

FILE: chartmodule/src/main/java/com/guannan/chartmodule/chart/ViewPortHandler.java
  class ViewPortHandler (line 10) | public class ViewPortHandler {
    method ViewPortHandler (line 27) | public ViewPortHandler() {
    method setChartDimens (line 34) | public void setChartDimens(int width, int height) {
    method setContentRatio (line 49) | public void setContentRatio(float ratio) {
    method restrainViewPort (line 56) | public void restrainViewPort(float offLeft, float offTop, float offRig...
    method getChartWidth (line 64) | public int getChartWidth() {
    method getChartHeight (line 71) | public int getChartHeight() {
    method offLeft (line 78) | public float offLeft() {
    method offTop (line 85) | public float offTop() {
    method offRight (line 92) | public float offRight() {
    method offBottom (line 99) | public float offBottom() {

FILE: chartmodule/src/main/java/com/guannan/chartmodule/data/ExtremeValue.java
  class ExtremeValue (line 8) | public class ExtremeValue {

FILE: chartmodule/src/main/java/com/guannan/chartmodule/data/KLineToDrawItem.java
  class KLineToDrawItem (line 12) | public class KLineToDrawItem implements Serializable {

FILE: chartmodule/src/main/java/com/guannan/chartmodule/data/LineRectItem.java
  class LineRectItem (line 9) | public class LineRectItem {

FILE: chartmodule/src/main/java/com/guannan/chartmodule/data/SubChartData.java
  class SubChartData (line 12) | public class SubChartData {
    method SubChartData (line 28) | public SubChartData() {
    method reset (line 37) | public void reset() {

FILE: chartmodule/src/main/java/com/guannan/chartmodule/data/TechItem.java
  class TechItem (line 8) | public class TechItem {

FILE: chartmodule/src/main/java/com/guannan/chartmodule/helper/ChartDataSourceHelper.java
  class ChartDataSourceHelper (line 24) | public class ChartDataSourceHelper {
    type SourceType (line 102) | public enum SourceType {
    method ChartDataSourceHelper (line 109) | public ChartDataSourceHelper(IChartDataCountListener<List<KLineToDrawI...
    method initKDrawData (line 117) | public void initKDrawData(List<KLineItem> klineList,
    method initKMoveDrawData (line 143) | public void initKMoveDrawData(float distance, SourceType sourceType) {
    method caculateBollPath (line 242) | private void caculateBollPath(float diffBoll, RectF contentRect, int i...
    method caculateMacdPath (line 276) | private void caculateMacdPath(float diffMacd, int index, int k, boolea...
    method resetDefaultValue (line 306) | private void resetDefaultValue() {
    method countStartEndPos (line 326) | private void countStartEndPos(float distance,
    method countMaxMinValue (line 355) | private ExtremeValue countMaxMinValue() {
    method getRect (line 413) | public RectF getRect(RectF parent, int col, float scaleTop,
    method getLine (line 434) | public RectF getLine(RectF parent, int col, float scaleTop,
    method getPoint (line 447) | public PointF getPoint(RectF rect, int col, float scaleY) {
    method getRect (line 454) | public RectF getRect(RectF techRect, int col, float scaleY) {

FILE: chartmodule/src/main/java/com/guannan/chartmodule/helper/ChartTouchHelper.java
  class ChartTouchHelper (line 24) | public class ChartTouchHelper
    type ChartGesture (line 58) | public enum ChartGesture {
    method ChartTouchHelper (line 124) | public ChartTouchHelper(View view) {
    method initVelocity (line 132) | private void initVelocity(Context context) {
    method setChartGestureListener (line 149) | public void setChartGestureListener(IChartGestureListener listener) {
    method startAction (line 156) | public void startAction(MotionEvent event) {
    method endAction (line 165) | public void endAction(MotionEvent event) {
    method onTouch (line 171) | @Override
    method onDoubleTap (line 243) | @Override
    method onLongPress (line 252) | @Override
    method onSingleTapUp (line 260) | @Override
    method onFling (line 274) | @Override
    method fling (line 281) | private void fling(float velocity, float offset) {
    method stopFling (line 299) | private void stopFling() {
    method onScroll (line 312) | @Override
    method getXDist (line 333) | private static float getXDist(MotionEvent e) {
    method spacing (line 341) | private static float spacing(MotionEvent event) {
    method midPoint (line 350) | private static void midPoint(PointF point, MotionEvent event) {
    method saveTouchStart (line 360) | private void saveTouchStart(MotionEvent event) {
    method distance (line 368) | protected static float distance(float eventX, float startX, float even...
    class FlingRunnable (line 374) | private static final class FlingRunnable implements Runnable {
      method FlingRunnable (line 388) | public FlingRunnable(int startX, int toX, int duration, Handler hand...
      method run (line 398) | @Override
      method stop (line 426) | public void stop() {

FILE: chartmodule/src/main/java/com/guannan/chartmodule/helper/TechParamType.java
  type TechParamType (line 8) | public enum TechParamType {

FILE: chartmodule/src/main/java/com/guannan/chartmodule/helper/TechParamsHelper.java
  class TechParamsHelper (line 14) | public class TechParamsHelper {
    class Limit (line 16) | public class Limit {
    method getTechItem (line 23) | private TechItem getTechItem(int index) {
    method getLimitValue (line 41) | public Limit getLimitValue(TechItem techItem, TechParamType techParamT...
    method caculateTechParams (line 66) | public void caculateTechParams(List<KLineItem> list, TechParamType tec...
    method linkDataBOLL (line 78) | private void linkDataBOLL(List<KLineItem> list) {
    method linkDataMACD (line 119) | public void linkDataMACD(List<KLineItem> list) {
    method _calcEMA (line 142) | public static float _calcEMA(float ema0_, float close_, int cycle_) {

FILE: chartmodule/src/main/java/com/guannan/chartmodule/inter/IChartDataCountListener.java
  type IChartDataCountListener (line 11) | public interface IChartDataCountListener<T> {
    method onReady (line 13) | void onReady(T data, ExtremeValue extremeValue, SubChartData subChartD...

FILE: chartmodule/src/main/java/com/guannan/chartmodule/inter/IChartGestureListener.java
  type IChartGestureListener (line 11) | public interface IChartGestureListener {
    method onChartGestureStart (line 19) | void onChartGestureStart(MotionEvent me, ChartTouchHelper.ChartGesture...
    method onChartGestureEnd (line 27) | void onChartGestureEnd(MotionEvent me, ChartTouchHelper.ChartGesture l...
    method onChartLongPressed (line 32) | void onChartLongPressed(MotionEvent me);
    method onChartDoubleTapped (line 37) | void onChartDoubleTapped(MotionEvent me);
    method onChartSingleTapped (line 42) | void onChartSingleTapped(MotionEvent me);
    method onChartFling (line 47) | void onChartFling(float distanceX);
    method onChartScale (line 55) | void onChartScale(MotionEvent me, float scaleX, float scaleY);
    method onChartTranslate (line 62) | void onChartTranslate(MotionEvent me, float dX);

FILE: chartmodule/src/main/java/com/guannan/chartmodule/inter/IPressChangeListener.java
  type IPressChangeListener (line 10) | public interface IPressChangeListener {
    method onChartTranslate (line 12) | void onChartTranslate(MotionEvent me, float dX);
    method onChartFling (line 14) | void onChartFling(float distanceX);
    method onChartScale (line 16) | void onChartScale(MotionEvent me, float scaleX, float scaleY);

FILE: chartmodule/src/main/java/com/guannan/chartmodule/inter/ITouchResponseListener.java
  type ITouchResponseListener (line 8) | public interface ITouchResponseListener {

FILE: chartmodule/src/main/java/com/guannan/chartmodule/utils/DataUtils.java
  class DataUtils (line 8) | public class DataUtils {
    method isZero (line 23) | public static boolean isZero(float val) {

FILE: chartmodule/src/main/java/com/guannan/chartmodule/utils/DateUtils.java
  class DateUtils (line 8) | public class DateUtils {
    method getMonth (line 10) | public static int getMonth(String date) {
    method getYMD (line 20) | public static String getYMD(String date) {

FILE: chartmodule/src/main/java/com/guannan/chartmodule/utils/DisplayUtils.java
  class DisplayUtils (line 12) | public class DisplayUtils {
    method dip2px (line 21) | public static int dip2px(Context context, float dpValue) {
    method px2dip (line 30) | public static int px2dip(Context context, float pxValue) {
    method getDisplayMetrics (line 36) | public static DisplayMetrics getDisplayMetrics(Context activity) {
    method getDisPlayMaxWidth (line 46) | public static int getDisPlayMaxWidth(Activity activity) {
    method getDisPlayWidth (line 65) | public static int getDisPlayWidth(Activity activity) {

FILE: chartmodule/src/main/java/com/guannan/chartmodule/utils/LogUtils.java
  class LogUtils (line 10) | public class LogUtils {
    method d (line 14) | public static void d(String msg) {

FILE: chartmodule/src/main/java/com/guannan/chartmodule/utils/NumFormatUtils.java
  class NumFormatUtils (line 10) | public class NumFormatUtils {
    method formatFloat (line 12) | public static float formatFloat(float d, int digits) {
    method formatBigFloatAll (line 26) | public static String formatBigFloatAll(float d, int digits) {
    method formatFloat (line 52) | public static String formatFloat(float d, int digits, boolean isAddPer...

FILE: chartmodule/src/main/java/com/guannan/chartmodule/utils/PaintUtils.java
  class PaintUtils (line 12) | public class PaintUtils {
    method init (line 146) | public static void init(Context context) {

FILE: chartmodule/src/main/java/com/guannan/chartmodule/utils/StringUtils.java
  class StringUtils (line 8) | public class StringUtils {
    method getAppendStr (line 13) | public static String getAppendStr(String arg1, float arg2) {
    method getAppendStr (line 23) | public static String getAppendStr(String arg1, String arg2) {

FILE: simulateddata/src/main/java/com/guannan/simulateddata/LocalUtils.java
  class LocalUtils (line 11) | public class LocalUtils {
    method getFromAssets (line 16) | public static String getFromAssets(Context context, String fileName) {

FILE: simulateddata/src/main/java/com/guannan/simulateddata/entity/KLineItem.java
  class KLineItem (line 8) | public class KLineItem {

FILE: simulateddata/src/main/java/com/guannan/simulateddata/parser/KLineParser.java
  class KLineParser (line 15) | public class KLineParser {
    method KLineParser (line 27) | public KLineParser(String klineJson) {
    method parseKlineData (line 34) | public void parseKlineData() {
    method getKLineItem (line 51) | public KLineItem getKLineItem(JSONObject obj) {
Condensed preview — 72 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (2,130K chars).
[
  {
    "path": ".gitignore",
    "chars": 229,
    "preview": "*.iml\n.gradle\n.idea\n*.iml\n*/build/\n/local.properties\n/.idea/caches\n/.idea/libraries\n/.idea/modules.xml\n/.idea/workspace."
  },
  {
    "path": "README.md",
    "chars": 1495,
    "preview": "# StockChart\n自定义股票行情图,高仿某币app行情图**(持续更新中!!!)**具体绘制细节参考博客:\nhttps://blog.csdn.net/kemeng7758/article/details/106729748\n# 功"
  },
  {
    "path": "app/.gitignore",
    "chars": 7,
    "preview": "/build\n"
  },
  {
    "path": "app/build.gradle",
    "chars": 794,
    "preview": "apply plugin: 'com.android.application'\n\nandroid {\n    compileSdkVersion 29\n    buildToolsVersion \"29.0.2\"\n    defaultCo"
  },
  {
    "path": "app/proguard-rules.pro",
    "chars": 751,
    "preview": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguar"
  },
  {
    "path": "app/src/androidTest/java/com/guannan/stockchart/ExampleInstrumentedTest.java",
    "chars": 741,
    "preview": "package com.guannan.stockchart;\n\nimport android.content.Context;\nimport androidx.test.platform.app.InstrumentationRegist"
  },
  {
    "path": "app/src/main/AndroidManifest.xml",
    "chars": 650,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n  package=\"c"
  },
  {
    "path": "app/src/main/java/com/guannan/stockchart/MainActivity.java",
    "chars": 4630,
    "preview": "package com.guannan.stockchart;\n\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.view.MotionEvent;\ni"
  },
  {
    "path": "app/src/main/res/drawable/ic_launcher_background.xml",
    "chars": 5012,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n  android:widt"
  },
  {
    "path": "app/src/main/res/drawable/selector_line_bg.xml",
    "chars": 642,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n  <item and"
  },
  {
    "path": "app/src/main/res/drawable-v24/ic_launcher_foreground.xml",
    "chars": 1712,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n  xmlns:aapt=\"http://schemas.android.com/aapt\"\n  andr"
  },
  {
    "path": "app/src/main/res/layout/activity_main.xml",
    "chars": 2785,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n  androi"
  },
  {
    "path": "app/src/main/res/layout/pankou_layout.xml",
    "chars": 2254,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n  andr"
  },
  {
    "path": "app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
    "chars": 268,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n  <bac"
  },
  {
    "path": "app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
    "chars": 268,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n  <bac"
  },
  {
    "path": "app/src/main/res/values/colors.xml",
    "chars": 202,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n  <color name=\"colorPrimary\">#008577</color>\n  <color name=\"colorPrim"
  },
  {
    "path": "app/src/main/res/values/strings.xml",
    "chars": 71,
    "preview": "<resources>\n  <string name=\"app_name\">StockChart</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/styles.xml",
    "chars": 361,
    "preview": "<resources>\n\n  <!-- Base application theme. -->\n  <style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.DarkActionBar\">\n "
  },
  {
    "path": "app/src/test/java/com/guannan/stockchart/ExampleUnitTest.java",
    "chars": 373,
    "preview": "package com.guannan.stockchart;\n\nimport org.junit.Test;\n\nimport static org.junit.Assert.*;\n\n/**\n * Example local unit te"
  },
  {
    "path": "build.gradle",
    "chars": 558,
    "preview": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\n\nbuildscript {\n    r"
  },
  {
    "path": "chartmodule/.gitignore",
    "chars": 7,
    "preview": "/build\n"
  },
  {
    "path": "chartmodule/build.gradle",
    "chars": 656,
    "preview": "apply plugin: 'com.android.library'\n\nandroid {\n  compileSdkVersion 29\n  buildToolsVersion \"29.0.2\"\n\n  defaultConfig {\n  "
  },
  {
    "path": "chartmodule/consumer-rules.pro",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "chartmodule/proguard-rules.pro",
    "chars": 751,
    "preview": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguar"
  },
  {
    "path": "chartmodule/src/main/AndroidManifest.xml",
    "chars": 108,
    "preview": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n  package=\"com.guannan.chartmodule\" />\n"
  },
  {
    "path": "chartmodule/src/main/java/com/guannan/chartmodule/chart/BaseChartView.java",
    "chars": 8304,
    "preview": "package com.guannan.chartmodule.chart;\n\nimport android.content.Context;\nimport android.graphics.Bitmap;\nimport android.g"
  },
  {
    "path": "chartmodule/src/main/java/com/guannan/chartmodule/chart/KMasterChartView.java",
    "chars": 11687,
    "preview": "package com.guannan.chartmodule.chart;\n\nimport android.content.Context;\nimport android.graphics.Canvas;\nimport android.g"
  },
  {
    "path": "chartmodule/src/main/java/com/guannan/chartmodule/chart/KSubChartView.java",
    "chars": 9363,
    "preview": "package com.guannan.chartmodule.chart;\n\nimport android.content.Context;\nimport android.graphics.Canvas;\nimport android.g"
  },
  {
    "path": "chartmodule/src/main/java/com/guannan/chartmodule/chart/MarketFigureChart.java",
    "chars": 3557,
    "preview": "package com.guannan.chartmodule.chart;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android"
  },
  {
    "path": "chartmodule/src/main/java/com/guannan/chartmodule/chart/ViewPortHandler.java",
    "chars": 1535,
    "preview": "package com.guannan.chartmodule.chart;\n\nimport android.graphics.RectF;\n\n/**\n * @author guannan\n * @date on 2020-03-29 15"
  },
  {
    "path": "chartmodule/src/main/java/com/guannan/chartmodule/data/ExtremeValue.java",
    "chars": 496,
    "preview": "package com.guannan.chartmodule.data;\n\n/**\n * @author guannan\n * @date on 2020-04-25 14:10\n * @des 最大值、最小值\n */\npublic cl"
  },
  {
    "path": "chartmodule/src/main/java/com/guannan/chartmodule/data/KLineToDrawItem.java",
    "chars": 632,
    "preview": "package com.guannan.chartmodule.data;\n\nimport android.graphics.RectF;\nimport com.guannan.simulateddata.entity.KLineItem;"
  },
  {
    "path": "chartmodule/src/main/java/com/guannan/chartmodule/data/LineRectItem.java",
    "chars": 205,
    "preview": "package com.guannan.chartmodule.data;\n\nimport android.graphics.RectF;\n\n/**\n * @author guannan\n * @date on 2020-06-12 16:"
  },
  {
    "path": "chartmodule/src/main/java/com/guannan/chartmodule/data/SubChartData.java",
    "chars": 974,
    "preview": "package com.guannan.chartmodule.data;\n\nimport android.graphics.Path;\nimport java.util.ArrayList;\nimport java.util.List;\n"
  },
  {
    "path": "chartmodule/src/main/java/com/guannan/chartmodule/data/TechItem.java",
    "chars": 390,
    "preview": "package com.guannan.chartmodule.data;\n\n/**\n * @author guannan\n * @date on 2020-06-10 14:38\n * @des\n */\npublic class Tech"
  },
  {
    "path": "chartmodule/src/main/java/com/guannan/chartmodule/helper/ChartDataSourceHelper.java",
    "chars": 13336,
    "preview": "package com.guannan.chartmodule.helper;\n\nimport android.graphics.PointF;\nimport android.graphics.RectF;\nimport com.guann"
  },
  {
    "path": "chartmodule/src/main/java/com/guannan/chartmodule/helper/ChartTouchHelper.java",
    "chars": 11093,
    "preview": "package com.guannan.chartmodule.helper;\n\nimport android.content.Context;\nimport android.graphics.PointF;\nimport android."
  },
  {
    "path": "chartmodule/src/main/java/com/guannan/chartmodule/helper/TechParamType.java",
    "chars": 167,
    "preview": "package com.guannan.chartmodule.helper;\n\n/**\n * @author guannan\n * @date on 2020-06-12 12:12\n * @des 技术指标类型\n */\npublic e"
  },
  {
    "path": "chartmodule/src/main/java/com/guannan/chartmodule/helper/TechParamsHelper.java",
    "chars": 3675,
    "preview": "package com.guannan.chartmodule.helper;\n\nimport com.guannan.chartmodule.data.TechItem;\nimport com.guannan.chartmodule.ut"
  },
  {
    "path": "chartmodule/src/main/java/com/guannan/chartmodule/inter/IChartDataCountListener.java",
    "chars": 344,
    "preview": "package com.guannan.chartmodule.inter;\n\nimport com.guannan.chartmodule.data.ExtremeValue;\nimport com.guannan.chartmodule"
  },
  {
    "path": "chartmodule/src/main/java/com/guannan/chartmodule/inter/IChartGestureListener.java",
    "chars": 1241,
    "preview": "package com.guannan.chartmodule.inter;\n\nimport android.view.MotionEvent;\nimport com.guannan.chartmodule.helper.ChartTouc"
  },
  {
    "path": "chartmodule/src/main/java/com/guannan/chartmodule/inter/IPressChangeListener.java",
    "chars": 342,
    "preview": "package com.guannan.chartmodule.inter;\n\nimport android.view.MotionEvent;\n\n/**\n * @author guannan\n * @date on 2020-04-21 "
  },
  {
    "path": "chartmodule/src/main/java/com/guannan/chartmodule/inter/ITouchResponseListener.java",
    "chars": 154,
    "preview": "package com.guannan.chartmodule.inter;\n\n/**\n * @author guannan\n * @date on 2020-04-21 20:01\n * @des 各种手势\n */\npublic inte"
  },
  {
    "path": "chartmodule/src/main/java/com/guannan/chartmodule/utils/DataUtils.java",
    "chars": 389,
    "preview": "package com.guannan.chartmodule.utils;\n\n/**\n * @author guannan\n * @date on 2020-04-08 20:02\n * @des 常量\n */\npublic class "
  },
  {
    "path": "chartmodule/src/main/java/com/guannan/chartmodule/utils/DateUtils.java",
    "chars": 557,
    "preview": "package com.guannan.chartmodule.utils;\n\n/**\n * @author guannan\n * @date on 2020-04-11 14:11\n * @des 日期相关处理\n */\npublic cl"
  },
  {
    "path": "chartmodule/src/main/java/com/guannan/chartmodule/utils/DisplayUtils.java",
    "chars": 2124,
    "preview": "package com.guannan.chartmodule.utils;\n\nimport android.app.Activity;\nimport android.content.Context;\nimport android.util"
  },
  {
    "path": "chartmodule/src/main/java/com/guannan/chartmodule/utils/LogUtils.java",
    "chars": 285,
    "preview": "package com.guannan.chartmodule.utils;\n\nimport android.util.Log;\n\n/**\n * @author guannan\n * @date on 2020-03-14 10:43\n *"
  },
  {
    "path": "chartmodule/src/main/java/com/guannan/chartmodule/utils/NumFormatUtils.java",
    "chars": 1946,
    "preview": "package com.guannan.chartmodule.utils;\n\nimport java.math.BigDecimal;\n\n/**\n * @author guannan\n * @date on 2020-04-11 16:0"
  },
  {
    "path": "chartmodule/src/main/java/com/guannan/chartmodule/utils/PaintUtils.java",
    "chars": 4460,
    "preview": "package com.guannan.chartmodule.utils;\n\nimport android.content.Context;\nimport android.graphics.Paint;\nimport android.ut"
  },
  {
    "path": "chartmodule/src/main/java/com/guannan/chartmodule/utils/StringUtils.java",
    "chars": 682,
    "preview": "package com.guannan.chartmodule.utils;\n\n/**\n * @author guannan\n * @date on 2020-06-10 19:54\n * @des 字符串操作相关\n */\npublic c"
  },
  {
    "path": "chartmodule/src/main/res/values/colors.xml",
    "chars": 234,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n  <!--红涨绿跌:红-->\n  <color name=\"color_fd4331\">#fd4331</color>\n  <!--红"
  },
  {
    "path": "chartmodule/src/main/res/values/strings.xml",
    "chars": 314,
    "preview": "<resources>\n  <string name=\"app_name\">chartModule</string>\n  <string name=\"date\">日期:</string>\n  <string name=\"open\">开:</"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "chars": 232,
    "preview": "#Fri Feb 21 13:20:34 CST 2020\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_"
  },
  {
    "path": "gradle.properties",
    "chars": 1073,
    "preview": "# Project-wide Gradle settings.\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will ov"
  },
  {
    "path": "gradlew",
    "chars": 5296,
    "preview": "#!/usr/bin/env sh\n\n##############################################################################\n##\n##  Gradle start up"
  },
  {
    "path": "gradlew.bat",
    "chars": 2260,
    "preview": "@if \"%DEBUG%\" == \"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@r"
  },
  {
    "path": "settings.gradle",
    "chars": 79,
    "preview": "include ':app', ':chartmodule', ':simulateddata'\nrootProject.name='StockChart'\n"
  },
  {
    "path": "simulateddata/.gitignore",
    "chars": 7,
    "preview": "/build\n"
  },
  {
    "path": "simulateddata/build.gradle",
    "chars": 687,
    "preview": "apply plugin: 'com.android.library'\n\nandroid {\n  compileSdkVersion 29\n  buildToolsVersion \"29.0.2\"\n\n  defaultConfig {\n  "
  },
  {
    "path": "simulateddata/consumer-rules.pro",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "simulateddata/proguard-rules.pro",
    "chars": 751,
    "preview": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguar"
  },
  {
    "path": "simulateddata/src/main/AndroidManifest.xml",
    "chars": 110,
    "preview": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n  package=\"com.guannan.simulateddata\" />\n"
  },
  {
    "path": "simulateddata/src/main/assets/geli.json",
    "chars": 559096,
    "preview": "[{\"day\":\"1996-11-18T00:00:00.000Z\",\"open\":17.5,\"high\":55,\"low\":17.5,\"close\":50,\"volume\":6278300},{\"day\":\"1996-11-19T00:0"
  },
  {
    "path": "simulateddata/src/main/assets/maotai.json",
    "chars": 465179,
    "preview": "[{\"day\":\"2001-08-27T00:00:00.000Z\",\"open\":34.51,\"high\":37.78,\"low\":32.85,\"close\":35.55,\"volume\":40631800},{\"day\":\"2001-0"
  },
  {
    "path": "simulateddata/src/main/assets/pingan.json",
    "chars": 327157,
    "preview": "[{\"day\":\"2007-03-01T00:00:00.000Z\",\"open\":50,\"high\":50.97,\"low\":45.8,\"close\":46.79,\"volume\":197763351},{\"day\":\"2007-03-0"
  },
  {
    "path": "simulateddata/src/main/assets/slw_k.json",
    "chars": 405690,
    "preview": "[{\"day\":\"2003-03-11T00:00:00.000Z\",\"open\":20.1,\"high\":21.36,\"low\":20.1,\"close\":21.17,\"volume\":14623537},{\"day\":\"2003-03-"
  },
  {
    "path": "simulateddata/src/main/java/com/guannan/simulateddata/LocalUtils.java",
    "chars": 767,
    "preview": "package com.guannan.simulateddata;\n\nimport android.content.Context;\nimport java.io.BufferedReader;\nimport java.io.InputS"
  },
  {
    "path": "simulateddata/src/main/java/com/guannan/simulateddata/entity/KLineItem.java",
    "chars": 452,
    "preview": "package com.guannan.simulateddata.entity;\n\n/**\n * @author guannan\n * @date on 2020-03-14 16:34\n * @des k线数据实体\n */\npublic"
  },
  {
    "path": "simulateddata/src/main/java/com/guannan/simulateddata/parser/KLineParser.java",
    "chars": 1917,
    "preview": "package com.guannan.simulateddata.parser;\n\nimport android.text.TextUtils;\nimport com.guannan.simulateddata.entity.KLineI"
  },
  {
    "path": "simulateddata/src/main/res/values/strings.xml",
    "chars": 74,
    "preview": "<resources>\n  <string name=\"app_name\">simulatedData</string>\n</resources>\n"
  }
]

// ... and 2 more files (download for full content)

About this extraction

This page contains the full source code of the SlamDunk007/StockChart GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 72 files (1.8 MB), approximately 845.2k tokens, and a symbol index with 194 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!