【问题标题】:Different EMA (Exponential Moving Average) than on Binance与 Binance 不同的 EMA(指数移动平均线)
【发布时间】:2021-04-19 23:24:08
【问题描述】:

我正在编写一个 Java 程序,使用 Binance JAVA API 检索交易对的 1 分钟间隔烛台。使用this Java 类,我想计算过去 10 天的 EMA(指数移动平均线)。

Binance JAVA API websocket 实现获取最新的深度事件,其中还包含我用来通过调用EMA#update 方法更新EMA 计算的当前收盘价。

但是,我注意到 Binance 图表上显示的 EMA 与我从代码中得到的不对应。此外,我注意到与 Binance 上显示的值相比,这些值需要一些时间来“稳定”,然后才能给出(有些)相同的值。

在 TradingView 上,我找到了 a formula 来计算 EMA(显示的 EMA 值与 Binance 上的相同),但这与 EMA 类中使用的不同。但是,即使使用此公式,其值也与 Binance 上的值大不相同。

有人可以帮我找出问题所在以及如何获得相同的值吗?

更新 1:提供代码

import java.util.*;
import java.util.stream.Collectors;

import com.binance.api.client.BinanceApiClientFactory;
import com.binance.api.client.BinanceApiRestClient;
import com.binance.api.client.BinanceApiWebSocketClient;
import com.binance.api.client.domain.market.Candlestick;
import com.binance.api.client.domain.market.CandlestickInterval;
import core.util.text.DecimalFormat;
import core.util.text.StringUtil;

public class test_003
{
  private Map<Long, Candlestick> candlesticksCache = new TreeMap<>();

  private EMA EMA_10;
  private EMA EMA_20;

  public static void main(String[] pArgs)
  {
    new test_003();
  }

  private test_003()
  {
    Locale.setDefault(Locale.US);

    candlesticksCacheExample("ADAUSDT", CandlestickInterval.ONE_MINUTE);
  }

  private void candlesticksCacheExample(String symbol, CandlestickInterval interval)
  {
    initializeCandlestickCache(symbol, interval);

    startCandlestickEventStreaming(symbol, interval);
  }

  private void initializeCandlestickCache(String symbol, CandlestickInterval interval)
  {
    BinanceApiClientFactory factory = BinanceApiClientFactory.newInstance();
    BinanceApiRestClient client = factory.newRestClient();

    List<Candlestick> candlestickBars_10 = client.getCandlestickBars(symbol.toUpperCase(), interval, Integer.valueOf(11), null, null);
    List<Candlestick> candlestickBars_20 = client.getCandlestickBars(symbol.toUpperCase(), interval, Integer.valueOf(21), null, null);

    List<Double> closingPriceList_10 = candlestickBars_10.stream().map(c -> Double.valueOf(c.getClose())).collect(Collectors.toList());
    List<Double> closingPriceList_20 = candlestickBars_20.stream().map(c -> Double.valueOf(c.getClose())).collect(Collectors.toList());

    EMA_10 = new EMA(closingPriceList_10, Integer.valueOf(10));
    EMA_20 = new EMA(closingPriceList_20, Integer.valueOf(20));
  }

  private void startCandlestickEventStreaming(String symbol, CandlestickInterval interval)
  {
    BinanceApiClientFactory factory = BinanceApiClientFactory.newInstance();
    BinanceApiWebSocketClient client = factory.newWebSocketClient();

    client.onCandlestickEvent(symbol.toLowerCase(), interval, response -> {
      Long openTime = response.getOpenTime();
      Candlestick updateCandlestick = candlesticksCache.get(openTime);
      if (updateCandlestick == null)
      {
        // new candlestick
        updateCandlestick = new Candlestick();
      }

      // update candlestick with the stream data
      updateCandlestick.setOpenTime(response.getOpenTime());
      updateCandlestick.setOpen(response.getOpen());
      updateCandlestick.setLow(response.getLow());
      updateCandlestick.setHigh(response.getHigh());
      updateCandlestick.setClose(response.getClose());
      updateCandlestick.setCloseTime(response.getCloseTime());
      updateCandlestick.setVolume(response.getVolume());
      updateCandlestick.setNumberOfTrades(response.getNumberOfTrades());
      updateCandlestick.setQuoteAssetVolume(response.getQuoteAssetVolume());
      updateCandlestick.setTakerBuyQuoteAssetVolume(response.getTakerBuyQuoteAssetVolume());
      updateCandlestick.setTakerBuyBaseAssetVolume(response.getTakerBuyQuoteAssetVolume());

      // Store the updated candlestick in the cache
      candlesticksCache.put(openTime, updateCandlestick);

      double closingPrice = Double.valueOf(updateCandlestick.getClose());

      EMA_10.update(closingPrice);
      EMA_20.update(closingPrice);

      System.out.println(StringUtil.replacePlaceholders("Closing price: %1 | EMA(10): %2 - EMA(20): %3", response.getClose(),
                                                        DecimalFormat.format(EMA_10.get(), "#.#####"),
                                                        DecimalFormat.format(EMA_20.get(), "#.#####")));
    });
  }

  public class EMA
  {

    private double currentEMA;
    private final int period;
    private final double multiplier;
    private final List<Double> EMAhistory;
    private final boolean historyNeeded;
    private String fileName;


    public EMA(List<Double> closingPrices, int period)
    {
      this(closingPrices, period, false);
    }

    public EMA(List<Double> closingPrices, int period, boolean historyNeeded)
    {
      currentEMA = 0;
      this.period = period;
      this.historyNeeded = historyNeeded;
      this.multiplier = 2.0 / (double) (period + 1);
      this.EMAhistory = new ArrayList<>();
      init(closingPrices);
    }

    public double get()
    {
      return currentEMA;
    }

    public double getTemp(double newPrice)
    {
      return (newPrice - currentEMA) * multiplier + currentEMA;
    }

    public void init(List<Double> closingPrices)
    {
      if (period > closingPrices.size()) return;

      //Initial SMA
      for (int i = 0; i < period; i++)
      {
        currentEMA += closingPrices.get(i);
      }

      currentEMA = currentEMA / (double) period;
      if (historyNeeded) EMAhistory.add(currentEMA);
      //Dont use latest unclosed candle;
      for (int i = period; i < closingPrices.size() - 1; i++)
      {
        update(closingPrices.get(i));
      }
    }

    public void update(double newPrice)
    {
      // EMA = (Close - EMA(previousBar)) * multiplier + EMA(previousBar)
      currentEMA = (newPrice - currentEMA) * multiplier + currentEMA;

      if (historyNeeded) EMAhistory.add(currentEMA);
    }

    public int check(double newPrice)
    {
      return 0;
    }

    public String getExplanation()
    {
      return null;
    }

    public List<Double> getEMAhistory()
    {
      return EMAhistory;
    }

    public int getPeriod()
    {
      return period;
    }
  }
}

更新 2

【问题讨论】:

  • 我认为你可以使用像ta-lib.org/hdr_dev.html这样的开源库来交叉检查你的结果
  • 感谢您的建议。但是,我的 EMA 值仍然存在细微差别。你能通过运行我的代码来帮助我吗?
  • 您是否使用了足够的历史数据来进行 ema 计算?它应该是 ema 回溯的 3 倍左右以稳定该值。例如,如果 ema 的长度为 10,您将在第 10 根蜡烛上收到第一个值,但 ema 仅在第 30 根蜡烛附近被认为是“稳定的”。
  • @e2e4 我将历史数据调高到 Binance API 发回的最大限制,即每 1 分钟间隔 1000 根烛台。运行我的程序时,这些值与 Binance 显示的值相同。但 30 秒后,数值略有不同,一两分钟后,它们相差很大(见我上次更新中的图片)
  • 你找到答案了吗?我现在正在尝试第二天.. 找到了所有可用的公式,尝试了不同的 wights,我永远无法从 binance 获得 ema。如果我在 5 分钟后设法匹配它们,一切都会不同\

标签: java api moving-average binance tradingview-api


【解决方案1】:

我注意到实际上有一个比我的其他答案更简单的方法。但是,我将保留那个,因为它仍然可能与处理不可靠的连接相关,在这种连接中,您不一定总是依靠response 获得最终的烛台。

response.getBarFinal()) 方法允许测试您收到的响应是最终烛台还是只是中间烛台。如果您按如下方式更改您的代码,您的 EMA 将只会更新蜡烛的最终收盘价,因为它应该是:

if (response.getBarFinal())
{
    double closingPrice = Double.valueOf(updateCandlestick.getClose());
    EMA_10.update(closingPrice);
    EMA_20.update(closingPrice);
    
    System.out.println(StringUtil.replacePlaceholders("Closing price: %1 | EMA(10): %2 - EMA(20): %3", response.getClose(),
            DecimalFormat.format(EMA_10.get(), "#.#####"),
            DecimalFormat.format(EMA_20.get(), "#.#####")));

}

【讨论】:

    【解决方案2】:

    问题在于onCandlestickEvent 不仅在蜡烛完成时被调用,而且实际上每分钟调用多次(每 2 秒左右)。您在response 中收到的数据跨越从蜡烛打开到响应的事件时间的时间,无论蜡烛是否完成。

    要明白我的意思,您可以将 startCandlestickEventStreaming 方法中的 System.out() 语句替换为以下内容:

    System.out.println(response.getOpenTime() + ";" +
        response.getEventTime() + ";" +
        response.getCloseTime());
    

    您会看到蜡烛的收盘时间实际上是在未来。

    为了正确更新您的 EMA,您必须等到蜡烛实际完成。您可以将暂定蜡烛的开盘时间存储在成员变量中,检查自上次调用 onCandlestickEvent 以来它是否发生了变化,然后使用蜡烛的最终收盘值更新您的 EMA:

    client.onCandlestickEvent(symbol.toLowerCase(), interval, response -> {
        Long openTime = response.getOpenTime(); 
        Candlestick updateCandlestick = candlesticksCache.get(openTime);
        if (updateCandlestick == null)
        {
            // new candlestick
            updateCandlestick = new Candlestick();
        }
    
        // update candlestick with the stream data
        ...
        
        // Store the updated candlestick in the cache
        candlesticksCache.put(openTime, updateCandlestick);
    
        if (openTime > m_LastOpenTime)
        {
            // need to get the close of the PREVIOUS candle
            Candlestick previousCandle = candlesticksCache.get(m_LastOpenTime);
            double closingPrice = Double.valueOf(previousCandle.getClose());
            
            EMA_10.update(closingPrice);
            EMA_20.update(closingPrice);
    
            System.out.println(StringUtil.replacePlaceholders("Closing price: %1 | EMA(10): %2 - EMA(20): %3", response.getClose(),
                                                            DecimalFormat.format(EMA_10.get(), "#.#####"),
                                                            DecimalFormat.format(EMA_20.get(), "#.#####")));
    
            m_LastOpenTime = openTime;
        }
    });       
    
    

    您可能会在第一次响应时遇到异常,因为堆栈上还没有蜡烛,而且我们没有m_LastOpenTime。您可以在调用client.onCandlestickEvent()之前获取当前服务器时间:

    private void startCandlestickEventStreaming(String symbol, CandlestickInterval interval)
    {
        BinanceApiClientFactory factory = BinanceApiClientFactory.newInstance();
        BinanceApiWebSocketClient client = factory.newWebSocketClient();
    
        BinanceApiRestClient restClient = factory.newRestClient();
        m_LastOpenTime = restClient.getServerTime();
    
        client.onCandlestickEvent(symbol.toLowerCase(), interval, response -> {
            ...
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2021-04-09
      • 2021-08-26
      • 2022-11-11
      • 2022-01-26
      • 1970-01-01
      • 2010-11-04
      • 1970-01-01
      • 1970-01-01
      • 2017-01-19
      相关资源
      最近更新 更多