Skip to content

Commit

Permalink
支持SAR指标
Browse files Browse the repository at this point in the history
  • Loading branch information
LLWenke committed Oct 13, 2023
1 parent 14cc557 commit a791ca0
Show file tree
Hide file tree
Showing 32 changed files with 1,261 additions and 370 deletions.
4 changes: 2 additions & 2 deletions .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 0 additions & 16 deletions .idea/modules.xml

This file was deleted.

38 changes: 26 additions & 12 deletions WKChart/build.gradle
Original file line number Diff line number Diff line change
@@ -1,24 +1,38 @@
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'

plugins {
id 'com.android.library'
id 'org.jetbrains.kotlin.android'
}
android {
compileSdkVersion 33
namespace 'com.wk.chart'
compileSdk 33
defaultConfig {
minSdkVersion 21
targetSdkVersion 33
versionCode 1
versionName "1.0"
}
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
}
buildFeatures {
viewBinding true
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}

dependencies {
implementation 'androidx.appcompat:appcompat:1.4.2'
implementation 'com.google.android.material:material:1.5.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
implementation 'androidx.recyclerview:recyclerview:1.2.1'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.9.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'androidx.recyclerview:recyclerview:1.3.0'
implementation 'com.github.CymChad:BaseRecyclerViewAdapterHelper:3.0.8'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4'
implementation 'com.google.code.gson:gson:2.9.0'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3'
implementation 'com.google.code.gson:gson:2.10.1'
}
5 changes: 4 additions & 1 deletion WKChart/src/main/java/com/wk/chart/ChartLayout.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import com.wk.chart.drawing.IndexLineDrawing;
import com.wk.chart.drawing.MACDDrawing;
import com.wk.chart.drawing.MarkerPointDrawing;
import com.wk.chart.drawing.SARDrawing;
import com.wk.chart.drawing.VolumeDrawing;
import com.wk.chart.drawing.WaterMarkingDrawing;
import com.wk.chart.drawing.candle.CandleDrawing;
Expand Down Expand Up @@ -102,8 +103,10 @@ protected void initCandleChartModules(AbsRender<?, ?> render) {
candleModule.addDrawing(new CandleDrawing());//蜡烛图组件
candleModule.addDrawing(new IndexLineDrawing(IndexType.CANDLE_MA));//MA组件
candleModule.addDrawing(new IndexLabelDrawing(IndexType.CANDLE_MA));//MA指标文字标签组件
candleModule.addDrawing(new IndexLineDrawing(IndexType.BOLL));//BOLL平均线组件
candleModule.addDrawing(new IndexLineDrawing(IndexType.BOLL));//BOLL指标组件
candleModule.addDrawing(new IndexLabelDrawing(IndexType.BOLL));//BOLL指标文字标签组件
candleModule.addDrawing(new SARDrawing());//SAR指标组件
candleModule.addDrawing(new IndexLabelDrawing(IndexType.SAR));//SAR指标文字标签组件
candleModule.addDrawing(new MarkerPointDrawing());//标记点绘制组件
candleModule.addDrawing(new ExtremumTagDrawing(ClickDrawingID.ID_EXTREMUM_TAG));//极值标签组件
candleModule.addDrawing(new BorderDrawing(PositionType.BOTTOM));//边框组件
Expand Down
236 changes: 173 additions & 63 deletions WKChart/src/main/java/com/wk/chart/adapter/CandleAdapter.java
Original file line number Diff line number Diff line change
Expand Up @@ -201,10 +201,11 @@ void buildData(@NonNull IndexBuildConfig buildConfig, @NonNull List<CandleEntry>
//构建数据属性值
buildScaleValue(data, startPosition);
buildTimeText(data, startPosition);
//计算 MA MACD BOLL RSI KDJ WR 指标
//计算 MA MACD BOLL SAR RSI KDJ WR 指标
computeMA(data, buildConfig, startPosition);
computeMACD(data, buildConfig, startPosition);
computeBOLL(data, buildConfig, startPosition);
computeSAR(data, buildConfig, startPosition);
computeRSI(data, buildConfig, startPosition);
computeKDJ(data, buildConfig, startPosition);
computeWR(data, buildConfig, startPosition);
Expand Down Expand Up @@ -285,6 +286,171 @@ private void computeMA(@NonNull List<CandleEntry> data, @NonNull IndexBuildConfi
}
}

/**
* BOLL(n)计算公式:
* MA=n日内的收盘价之和÷n。
* MD=n日的平方根(C-MA)的两次方之和除以n
* MB=n日的MA
* UP=MB+p×MD
* DN=MB-p×MD
* p为参数,可根据股票的特性来做相应的调整,一般默认为2
*
* @param data 数据集合
* @param n 周期,一般为26
* @param p 参数,可根据股票的特性来做相应的调整,一般默认为2
*/
public void calculateBOLL(@NonNull List<CandleEntry> data, int n, int p, int startPosition) {
long bollMA = startPosition == 0 ? 0 : calculationCache.bollMA;
int position = n - 1;
for (int i = startPosition, z = data.size(); i < z; i++) {
CandleEntry entry = data.get(i);
bollMA += entry.getClose().result;
if (i >= position) {
if (i > position) {
bollMA -= data.get(i - n).getClose().result;
}
//n日MA
long maValue = bollMA / n;
long md = 0;
for (int j = i - position; j <= i; j++) {
//n日
long value = data.get(j).getClose().result - maValue;
md += value * value;
}
md = md / n;
md = (long) Math.sqrt(md);
long up = maValue + p * md;//上轨线
long dn = maValue - p * md;//下轨线
//精度恢复运算
ValueEntry[] bollValues = new ValueEntry[3];
bollValues[0] = entry.buildQuoteScaleValue(getScale(), up);
bollValues[1] = entry.buildQuoteScaleValue(getScale(), maValue);
bollValues[2] = entry.buildQuoteScaleValue(getScale(), dn);
//存储此次计算结果
entry.putLineIndex(IndexType.BOLL, bollValues);
}
//将倒数第二次的计算结果缓存
if (i == z - 2) {
this.calculationCache.bollMA = bollMA;
}
}
}

/**
* 计算 BOLL
*/
private void computeBOLL(@NonNull List<CandleEntry> data, @NonNull IndexBuildConfig indicatorConfig, int startPosition) {
IndexConfigEntry indicatorTag = indicatorConfig.getIndexTags(IndexType.BOLL);
if (null == indicatorTag || indicatorTag.getFlagEntries().length < 2) {
return;
}
calculateBOLL(data, indicatorTag.getFlagEntries()[0].getFlag(), indicatorTag.getFlagEntries()[1].getFlag(), startPosition);
}

/**
* 计算 SAR
* SAR(i)= SAR(i-1)+(EP(i-1)- SAR(i-1))× AF(i)
* SAR(N,S,M),N为计算周期,S为步长,M为极值
* SAR(4,0.02,0.2)表示计算4日抛物转向,步长为2%,极限值为20%
*
* @param data 数据集合
* @param n 周期 一般为4
* @param s 步长 一般为2
* @param m 极限值 一般为20
*/
private void calculateSAR(@NonNull List<CandleEntry> data, int n, int s, int m, int startPosition) {
long ep, af, sar, prevHigh, prevLow;
if (startPosition == 0) {
ep = 0;
af = 0;
sar = 0;
prevHigh = 0;
prevLow = 0;
} else {
ep = calculationCache.ep;
af = calculationCache.af;
sar = calculationCache.sar;
prevHigh = calculationCache.prevHigh;
prevLow = calculationCache.prevLow;
}
int position = n - 1;
double scale = ValueUtils.pow10(2);
for (int i = startPosition, z = data.size(); i < z; i++) {
if (i >= position) {
boolean isRise;
int firstIndex = i - position;
long high = Long.MIN_VALUE, low = Long.MAX_VALUE;
CandleEntry entry = data.get(i);
//计算周期内最高价和最低价
for (int j = firstIndex; j <= i; j++) {
CandleEntry item = data.get(j);
high = Math.max(item.getHigh().result, high);
low = Math.min(item.getLow().result, low);
}
if (i == position) {//若是看涨,则第一天的SAR值必须是周期内的最低价;若是看跌,则第一天的SAR须是周期的最高价。
isRise = entry.getClose().result >= data.get(firstIndex).getClose().result;
sar = isRise ? low : high;
} else {//第二天的SAR,则为前一天的最高价(看涨时)或是最低价(看跌时)与前一天的SAR的差距乘上加速因子,再加上前一天的SAR就可求得。
isRise = entry.getClose().result >= sar;
if (isRise) {
af += entry.getHigh().result > prevHigh ? s : 0;
} else {
af += entry.getLow().result < prevLow ? s : 0;
}
af = af > m ? s : af;
sar = (long) (sar + (ep - sar) * (af / scale));
}
//SAR反转点判断
if (isRise) {
if (entry.getLow().result <= sar) {//如果是上涨趋势,当前最低价小于等于SAR,表明SAR触碰了当前最低点,反转
//上涨反转到下跌:SAR=周期内最高价,EP=周期内最低价,AF=0
sar = high;
ep = low;
af = 0;
} else {
ep = high;
}
} else {
if (entry.getHigh().result >= sar) {//如果是下跌趋势,当前最高价大于等于SAR,表明SAR触碰了当前最高点,反转
//下跌反转到上涨:SAR=周期内最低价,EP=周期内最高价,AF=0
sar = low;
ep = high;
af = 0;
} else {
ep = low;
}
}
prevHigh = high;
prevLow = low;
//精度恢复运算
ValueEntry[] sarValues = new ValueEntry[1];
sarValues[0] = entry.buildQuoteScaleValue(getScale(), sar);
//存储此次计算结果
entry.putIndex(IndexType.SAR, sarValues);
}
//将倒数第二次的计算结果缓存
if (i == z - 2) {
calculationCache.ep = ep;
calculationCache.af = af;
calculationCache.sar = sar;
calculationCache.prevHigh = prevHigh;
calculationCache.prevLow = prevLow;
}
}
}

/**
* 计算 SAR
*/
private void computeSAR(@NonNull List<CandleEntry> data, @NonNull IndexBuildConfig indicatorConfig, int startPosition) {
IndexConfigEntry indicatorTag = indicatorConfig.getIndexTags(IndexType.SAR);
if (null == indicatorTag || indicatorTag.getFlagEntries().length < 3) {
return;
}
calculateSAR(data, indicatorTag.getFlagEntries()[0].getFlag(), indicatorTag.getFlagEntries()[1].getFlag()
, indicatorTag.getFlagEntries()[2].getFlag(), startPosition);
}

/**
* 计算 MACD
* {12, 26, 9},
Expand Down Expand Up @@ -363,68 +529,6 @@ private void calculateMACD(@NonNull List<CandleEntry> data, int s, int l, int m,
}
}


/**
* BOLL(n)计算公式:
* MA=n日内的收盘价之和÷n。
* MD=n日的平方根(C-MA)的两次方之和除以n
* MB=n日的MA
* UP=MB+p×MD
* DN=MB-p×MD
* p为参数,可根据股票的特性来做相应的调整,一般默认为2
*
* @param data 数据集合
* @param n 周期,一般为26
* @param p 参数,可根据股票的特性来做相应的调整,一般默认为2
*/
public void calculateBOLL(@NonNull List<CandleEntry> data, int n, int p, int startPosition) {
long bollMA = startPosition == 0 ? 0 : calculationCache.bollMA;
int position = n - 1;
for (int i = startPosition, z = data.size(); i < z; i++) {
CandleEntry entry = data.get(i);
bollMA += entry.getClose().result;
if (i >= position) {
if (i > position) {
bollMA -= data.get(i - n).getClose().result;
}
//n日MA
long maValue = bollMA / n;
long md = 0;
for (int j = i - position; j <= i; j++) {
//n日
long value = data.get(j).getClose().result - maValue;
md += value * value;
}
md = md / n;
md = (long) Math.sqrt(md);
long up = maValue + p * md;//上轨线
long dn = maValue - p * md;//下轨线
//精度恢复运算
ValueEntry[] bollValues = new ValueEntry[3];
bollValues[0] = entry.buildQuoteScaleValue(getScale(), up);
bollValues[1] = entry.buildQuoteScaleValue(getScale(), maValue);
bollValues[2] = entry.buildQuoteScaleValue(getScale(), dn);
//存储此次计算结果
entry.putLineIndex(IndexType.BOLL, bollValues);
}
//将倒数第二次的计算结果缓存
if (i == z - 2) {
this.calculationCache.bollMA = bollMA;
}
}
}

/**
* 计算 BOLL
*/
private void computeBOLL(@NonNull List<CandleEntry> data, @NonNull IndexBuildConfig indicatorConfig, int startPosition) {
IndexConfigEntry indicatorTag = indicatorConfig.getIndexTags(IndexType.BOLL);
if (null == indicatorTag || indicatorTag.getFlagEntries().length < 2) {
return;
}
calculateBOLL(data, indicatorTag.getFlagEntries()[0].getFlag(), indicatorTag.getFlagEntries()[1].getFlag(), startPosition);
}

/**
* 计算 RSI
*/
Expand Down Expand Up @@ -577,5 +681,11 @@ static class CalculationCache {
//KDJ
long k = 0;
long d = 0;
//SAR
long ep = 0;
long af = 0;
long sar = 0;
public long prevLow = 0;
public long prevHigh = 0;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ public BaseAttribute(@NonNull Context context) {
/**
* 共用的有关属性
*/
public float pointSize = 6f; //数据点大小
public float lineWidth = 3f; //线条宽度
public int lineColor = 0x1Affffff; // 线条颜色
public float labelSize = 26; // 标签字符大小
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,5 @@ public CandleAttribute(@NonNull Context context) {
*/
public float timeLineWidth = 3f; // 分时线宽度
public int timeLineColor = 0xFF52649C; // 分时线颜色

}
Loading

0 comments on commit a791ca0

Please sign in to comment.