public void calculate(CoinList list) {
    if (day <= 0 || skipDay < 0) {
      Object oldValue = this.value;
      this.value = null;
      if (equals(oldValue, value) == false) {
        this.firePropertyChange("value", oldValue, this.value);
      }
      return;
    }

    List<Coin> stocks = new ArrayList<Coin>();
    List<Double> values = new ArrayList<Double>();

    List<Double> highs = new ArrayList<Double>();
    List<Double> lows = new ArrayList<Double>();
    List<Double> closes = new ArrayList<Double>();

    List<Long> volumes = new ArrayList<Long>();

    final int size = list.size();

    final int start = Math.max(0, size - getRequiredHistorySize());

    for (int i = Math.max(0, start - skipDay), ei = Math.max(0, size - skipDay); i < ei; i++) {
      final long timestamp = list.getTimestamp(i);
      final Coin stock = list.getCoinTime(timestamp);
      stocks.add(stock);
    }

    if (this.function == Function.MFI) {
      for (Coin stock : stocks) {
        values.add(AnalysisData.getTypicalPrice(stock));
        highs.add(stock.getHighPrice());
        lows.add(stock.getLowPrice());
        closes.add(stock.getLastPrice());
        volumes.add(stock.getVolume());
      }
    } else {
      switch (type) {
        case PrevPrice:
          for (Coin stock : stocks) {
            values.add(stock.getPrevPrice());
          }
          break;

        case OpenPrice:
          for (Coin stock : stocks) {
            values.add(stock.getOpenPrice());
          }
          break;

        case HighPrice:
          for (Coin stock : stocks) {
            values.add(stock.getHighPrice());
          }
          break;

        case LowPrice:
          for (Coin stock : stocks) {
            values.add(stock.getLowPrice());
          }
          break;

        case LastPrice:
          for (Coin stock : stocks) {
            values.add(stock.getLastPrice());
          }
          break;

        case TypicalPrice:
          for (Coin stock : stocks) {
            values.add(AnalysisData.getTypicalPrice(stock));
          }
          break;

        case Volume:
          for (Coin stock : stocks) {
            values.add(new Double(stock.getVolume()));
          }
          break;
        case Issued:
          values.add(new Double(list.getIssuedCount()));
          break;

        default:
          assert (false);
      }
    }

    final int dataSize = values.size();

    if (dataSize == 0) {
      Object oldValue = this.value;
      this.value = null;
      if (equals(oldValue, value) == false) {
        this.firePropertyChange("value", oldValue, this.value);
      }
      return;
    }

    Double v = function == Function.Min ? Double.MAX_VALUE : 0.0;
    double tmp_v = v;

    switch (function) {
      case Max:
        for (Double _value : values) {
          tmp_v = Math.max(tmp_v, _value);
        }
        v = tmp_v;
        break;

      case Min:
        for (Double _value : values) {
          tmp_v = Math.min(tmp_v, _value);
        }
        v = tmp_v;
        break;

      case Average:
        v = average(values);
        break;

      case MeanDeviation:
        double average = 0;
        for (Double _value : values) {
          average = average + _value;
        }
        average = average / (double) dataSize;
        for (Double _value : values) {
          tmp_v = tmp_v + Math.abs(_value - average);
        }
        tmp_v = tmp_v / (double) dataSize;
        v = tmp_v;
        break;

      case RSI:
        v = AnalysisData.createRSI(values, day);
        break;

      case EMA:
        v = AnalysisData.createEMA(values, day);
        break;

      case MFI:
        v = AnalysisData.createMFI(highs, lows, closes, volumes, day);
        break;

      case MACD:
        v = AnalysisData.createMACDFix(values, day).outMACD;
        break;

      case MACDSignal:
        v = AnalysisData.createMACDFix(values, day).outMACDSignal;
        break;

      case MACDHist:
        v = AnalysisData.createMACDFix(values, day).outMACDHist;
        break;

      default:
        assert (false);
    }

    Object oldValue = this.value;

    this.value = v;

    if (equals(oldValue, value) == false) {
      this.firePropertyChange("value", oldValue, this.value);
    }
  }
 public static double getTypicalPrice(Coin coin) {
   return (coin.getHighPrice() + coin.getLowPrice() + coin.getLastPrice()) / 3.0d;
 }