Total Pageviews

2016/11/01

[閱讀筆記] Security Analysis (Part 5)


  1. income statement (損益表) 中數字的意思並無法被全然理解,除非你參照 balance sheet (資產負債表) 一起看
  2. 在考察公司的盈利能力時,不僅要看公司的利潤金額,還要看公司的利潤結構,即區分利潤中哪些是可以持續獲得的,哪些是偶然獲得的。那些偶然獲得的,就是非經常項目(nonrecurrent items)
  3. 好的存貨是資產,不好的存貨是負債。企業想美化財報、提高獲利,若無法提高營收,就只能從降低成本來下手。而存貨正是企業用來調節成本、隱藏損失、藏污納垢、掩人耳目的最佳暫存區
  4. 存貨一旦售出,資產就發揮效用創造獲利,這時存貨成本就轉成銷貨成本,也就是從資產負債表的科目轉換到了損益表。如果企業在製作財報時,刻意把存貨當滯洪池來使用,就能適度的來調整損益了
  5. 公司銷售的產品若屬專案類型,營收與成本認列的時間點就在驗收日期。如專案剛好是在十二月驗收,而該專案因為成本控制不當導致專案虧損,若不想要讓此專案影響當年度獲利,只要跟客戶協商一下,把驗收日期挪到隔年一月才驗收,此虧損就不會在今年認列
  6. 企業在做存貨核算(inventory accounting)時,在預期存貨的價格不斷上漲時才選擇後進先出法,以達到遞延所得稅支出的目的,而當預期存貨價格將持續下跌時,則通過會計政策變更將存貨計價方法由後進先出法轉為先進先出法
  7. 按照會計制度的規定,企業的盈虧應當依照規定以損益表呈現。許多企業常透過資產評估,將壞賬、滯銷和毀損存貨、長期投資損失、固定資產損失以及遞延資產等潛虧確認為評估減值,沖抵“資本公積”,從而達到粉飾會計報表,虛增利潤的目的
  8. 當企業開始進行令人起疑的會計手段時,無論這家企業看起來多安全、多有吸引力,投資人還是要遠離這種公司
  9. Security Analysis 必須估到其子企業的營收,不管是賺錢或賠錢。有些公司會因為子企業虧損而不報,或者是雖然子企業賺錢的時候先不報,等日後母公司業績不是很好的時候才拿出來報
  10. 折舊的中心概念,就是把買機器、設備花的錢分攤到使用年限的時間內,這樣我們才能客觀的從財報看到公司費用的分攤與資產情況。如蛋糕店要把買烤箱 花的40萬元分成10份分攤到未來10年的損益之中
  11. 有些企業會利用折舊方法,來修飾自己的毛利率,如原本折舊年限從十年延長到二十年,如此每年攤提成本降低,毛利被拉高,但實際上公司並沒只有比較賺錢
  12. 折舊費用的高低可以檢視企業經營的狀況,如果一個公司折舊費用佔利潤比率過高,代表著必須一直將資金投入至先進的設備才能維持公司本身的競爭優勢。這會吞噬掉公司利潤,巴菲特認為不應該佔有太多的比率,否則一旦產業發生改變,將會造成巨幅的虧損
  13. 在研究營收紀錄時,security analysis有個重要的原則,只有在其資料有針對該公司的定性資料所支持下,其定量資料才是有用的
  14. 透過過去的營收記錄,是無法判斷這家公司是否穩定。你應該拋開數字,去研究其最根本的賺錢能力(earning power)
  15. 過去的營收趨勢向上,並不代表未來趨勢就一定向上,有可能競爭加劇、法規影響等,都會影響公司獲利
  16. 股票市場往往是先發生價格後,事後人們忙著找原因。股票市場的價格不是根據計算來的,是根據人們對事件的反應、掙扎而來。實務上來說,沒有一套公式或規則可算出一檔股票的proper value
  17. 盡管無法正確估出股票的proper value,但是你必須做到: (1) 有別於投機客,建立一套保守的投資估價方法 ;(2) 了解一家公司的資本結構與收入來源;(3) 找出資產負債表中不尋常的項目
  18. 我們建議,本益比20倍是購買一檔股票的上限
  19. 最近幾年的獲利狀況,可以作為預測未來營收的度量: 如果(1)最近幾年的市場狀況沒有特別的好;(2) 過去幾年營收的趨勢是往上的;(3) 投資人對於產業的研究讓他有信其公司會持續成長。這時,你才會比較有信心,這家公司未來是會成長的
  20. 無論你是投資客或是投機客,你的成績單是由未來的營收所決定,不是過去的營收
  21. price-earnings ratio (本益比) 無法提供margin of safety(安全邊際),price-earnings ratio (本益比)只能提供給投資者一個可能性,即本益比較低的股票,未來的營收可能會比過去好
  22. An attractive common-stock investment is an attractive speculation
  23. 我們建議,本益比 12 或 12.5 倍是一個比較適當的數據,這是一個必要的條件之一,還不夠滿足全部的條件,其條件還包含公司財務結構、領導階層的能力、公司與產業的未來展望等等
  24. 雖然我們不建議購買本益比超過20倍的股票,但是不是說購買的行為是錯誤的,而是這樣的股票相對投機,長期來說,比較容易賠錢
  25. 用高價未來買進績優股,本質是投機的,因為未來是否會持續成長,都是未知數
  26. 資本結構(Capitalization Structure) 可以從資產負債表得到相關的訊息,資本結構是負債與業主權益的比例,當負債越高,則風險就會越高
  27. 造成投機性資本結構的原因有:債券和優先股的比例比普通股大很多;高負債;生產成本高、利潤率低,市值比銷售額小很多倍。前兩點是因為必須在淨利潤之前扣除每年必須優先償還的利息,第三點是因為低的利潤率導致淨利潤對銷售額的變化極其敏感
  28. 好公司並不一定就是好投資,一個公司的資本結構直接影響到公司的收益,從而影響股價。公司利用槓桿的程度是不同的,有的偏於保守,有的偏於激進,很多股市奇蹟其實是這些具有投機性資本結構的股票造成的
  29. 價格介於10~20元的股票歸類於低價股。低價股的波動傾向大於高價股;在牛市,低價股通常漲的比高價股多,在熊市時,低價股跌的比高價股少
  30. Low-priced stock有分真的低價股或假的低價股,真的低價股是真實反映其營收不佳的事實;假的低價股是,公司內部已事先知道內線先予以拋售,所導致的low price,但此時不是真的low price,不知情的buyer逢低買進卻被套牢。大部份購買low-priced stock賠錢的原因是,它們是被倒貨的buyer,促成seller的利益
  31. 投資的行為是,經過徹底的分析,承諾本金的安全性與定義滿意的回報,若違反這些規則,就是投機,不是投資
  32. 股票的真實價值是經由以下事實所決定,包括資產、營收、股利、未來展望等;但是,市場的報價卻是建立在市場操作與人性
  33. 不適當或錯誤的資料,以及未來的不確定性,導致真實價值總是難以捉摸
  34. 投資永遠都要面對未來的不確定性,所以投資人再買進時,必須確認是否有足夠的Margin of safety 來彌補未來的不確定性
  35. Balance Sheet 可以指出與公司營運有緊密關聯的資源的數量與本質;提供分析公司收入來源的本質與穩定性;債務端顯示公司的財務狀況;可以與過去幾年的balance sheet比較,分析營收的品質,可以搭配透過現金流量表了解資金動向

2016/10/31

[Travel] 2016/10

行天宮
IMG_5478

松山文創園區
IMG_5483

南子吝山
DSC00812

DSC00809

DSC00800

2016/10/10

[Java 8] Using lambda to convert a List of object to comma-separated string

If I java an object which named Group
1
2
3
4
5
6
7
    import lombok.Data;
    
    @Data
    public class Group {    
        private Integer id;
        private String name;
    }

The data in List of Group looks like:
1
2
3
    Group(id=1, name=Group A)
    Group(id=2, name=Group B)
    Group(id=3, name=Group C)

The expected result is to convert to comma-separated string: 
Group A, Group B, Group c

If we does not utilize lambda expression, the code snippet is as following:
1
2
3
4
5
6
7
8
9
    List<Group> groups = dto.getUser().getGroups();
    StringBuilder groupStringBuilder = new StringBuilder();
    for (int i = 0; i < groups.size(); i++) {
        if (i == groups.size() - 1) {
            groupStringBuilder = groupStringBuilder.append(groups.get(i).getName());
        } else {
            groupStringBuilder = groupStringBuilder.append(groups.get(i).getName()).append(", ");
        }
    }


If we apply lambda expression, we only need to use one line of code to meet this requirement:
    String groupStr = groups.stream().map(g -> g.getName()).collect(Collectors.joining(", "));




2016/10/09

[Java 8] Convert a List of String to a Comma-Separated String

If I have a List of String looks like:
1
2
3
private List<String> getDummyPlayers() {
    return Arrays.asList("王 柏 融", "林 智 勝", "林 泓 育", "蔣 智 賢", "高 國 慶");
}

If I would like to convert it to String, and each player should separate with comma, i.e. 王 柏 融, 林 智 勝, 林 泓 育, 蔣 智 賢, 高 國 慶

Before Java 8, we may do this way:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
private String separatePlayersWithComma(){
    List<String> players =getDummyPlayers();
    String playersWithComma = "";
    for (int i = 0; i < players.size(); i++) {
        if (i == players.size() - 1) {
            playersWithComma = playersWithComma + players.get(i);
        } else {
            playersWithComma = playersWithComma + players.get(i) +", "; 
        }
    }
    return playersWithComma;
}


In Java 8, it is very easy and straightforward:
1
2
3
4
5
private String separatePlayersWithComma_usingJDK8(){
    List<String> players =getDummyPlayers();
    String playersWithComma = players.stream().collect(Collectors.joining(", "));
    return playersWithComma;
}






2016/10/08

[Project Lombok] @Cleanup

Project Lombok provide an easy way to ensure a given resource is automatically cleaned up before the code execution path exits your current scope. All you need to do is to annotate any local variable declaration with the @Cleanup annotation.


As-Is (Do not annotate @Cleanup)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
private void readExcelTest1(String excelFile) throws IOException {
     List<Issue> issues = new ArrayList<Issue>();

     InputStream inputStream = null;
     Workbook workbook = null;

     try {
         // 1. Create a Workbook.
         inputStream = new FileInputStream(new File(excelFile));

         // 2. Get first sheet
         workbook = new HSSFWorkbook(inputStream);

         Sheet sheet = workbook.getSheetAt(0);

         // 3. Iterate through each rows from first sheet
         Iterator<Row> rowIterator = sheet.iterator();
         int rowCount = 1;
         while (rowIterator.hasNext()) {
            // (1) ignore header row
            if (rowCount == 1) {
              rowIterator.next();
              rowCount++;
             }
             // (2) start to read each row from second row
             else {
                 Row row = rowIterator.next();
                 Integer id = Double.valueOf(row.getCell(0).getNumericCellValue()).intValue();
                 String subject = row.getCell(1).getStringCellValue();
                 String status = row.getCell(2).getStringCellValue();
                 String priority = row.getCell(3).getStringCellValue();
                 String notes = row.getCell(4).getStringCellValue();

                 Issue issue = new Issue();
                 issue.setId(id);
                 issue.setSubject(subject);
                 issue.setStatus(status);
                 issue.setPriority(priority);
                 issue.setNotes(notes);

                 issues.add(issue);
              }
           }

          // print collection
          issues.stream().forEach(issue -> log.debug(issue.toString()));
     } catch (FileNotFoundException e) {
         throw e;
     } catch (IOException e) {
         throw e;
     } finally {
         if (workbook != null) {
           workbook.close();
         }

         if (inputStream != null) {
           inputStream.close();
         }
     }
}



To-Be(Annotte with @Cleanup)
It will ensure the variable declaration that you annotate will be cleaned up by calling its close method, regardless of what happens. Implemented by wrapping all statements following the local variable declaration to the end of your scope into a try block that, as a finally action, closes the resource. 
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
 private void readExcelTest2(String excelFile) throws Exception {
     try {
         // 1. Create a Workbook.
         @Cleanup
         InputStream inputStream = new FileInputStream(new File(excelFile));

         // 2. Get first sheet
         @Cleanup
         Workbook workbook = new HSSFWorkbook(inputStream);

         List<Issue> issues = new ArrayList<Issue>();

         Sheet sheet = workbook.getSheetAt(0);
 
         // 3. Iterate through each rows from first sheet
         Iterator<Row> rowIterator = sheet.iterator();
         int rowCount = 1;
         while (rowIterator.hasNext()) {
              // (1) ignore header row
              if (rowCount == 1) {
              rowIterator.next();
              rowCount++;
              }
              // (2) start to read each row from second row
              else {
                  Row row = rowIterator.next();
                  Integer id = Double.valueOf(row.getCell(0).getNumericCellValue()).intValue();
                  String subject = row.getCell(1).getStringCellValue();
                  String status = row.getCell(2).getStringCellValue();
                  String priority = row.getCell(3).getStringCellValue();
                  String notes = row.getCell(4).getStringCellValue();

                  Issue issue = new Issue();
                  issue.setId(id);
                  issue.setSubject(subject);
                  issue.setStatus(status);
                  issue.setPriority(priority);
                  issue.setNotes(notes);
    
                  issues.add(issue);
              }
         }

         // print collection
         issues.stream().forEach(issue -> log.debug(issue.toString()));
    
     } catch (FileNotFoundException e) {
         throw e;
     } catch (Exception e) {
         throw e;
     }
 }



Reference
[1] https://projectlombok.org/features/Cleanup.html

2016/10/07

[PDFBox] Generate TIF with 0-kb

Problem
I am using Apache PDFBox to convert PDF to TIF file.

The scenario is to generate TIF file to a specific path, then another application will read this TIF file and move to another directory.



But it will generate TIF file with 0-kb at times and fail to open this TIF file, and does not have any abnormal message in application log.


How-To

Here is the code snippet:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
    private List<String> writeImage(PDDocument document, String outputPrefix, ImageType imageType,
            float dpi, String fileName) throws IOException {
        ImageIO.scanForPlugins();

        List<String> tifNames = new ArrayList<String>();

        PDFRenderer renderer = new PDFRenderer(document);

        for (int i = 0; i < document.getNumberOfPages(); i++) {

            BufferedImage image = renderer.renderImageWithDPI(i, dpi, imageType);

            String outputPostfix = "_" + String.format("%02d", i + 1); // 01開始
            String outputFileName = outputPrefix + outputPostfix;// 檔名: 保單號碼_0X

            tifNames.add(fileName + outputPostfix); // 回傳產了那些檔名

            ImageIOUtil.writeImage(image, outputFileName + "." + "tif", Math.round(dpi));
            image.flush();
        }
        return tifNames;
    }



We can see implementation details in ImageIOUtil.writeImage
https://github.com/apache/pdfbox/blob/04292e421e9531616aa2856b908fbdb05b381af7/tools/src/main/java/org/apache/pdfbox/tools/imageio/ImageIOUtil.java#L65-L79

We can see it will create a File instance at first, then write image to TIF file.


As it create a File instance, this file will be created. It another application had read this file, then this file may be locked and failed to write.

Therefore, we should create TIF file to a temporary directory first, then move the finished TIF file to the destination. 




2016/10/06

[Mac] Yahoo 輸入法的全型與半型的切換

當我在 Mac 平台使用 Yahoo 輸入法時,當我切換為英數字的輸入時,若為全型時,該如何轉換成半型


此時你只要 shift + command + space ,即可將全型轉成半型



輸入結果如下


若要從半形再轉回全形,你只要再按一次 shift + command + space

2016/10/05

[Mac] Yahoo 輸入法如何中英切換

我在mac平台,使用yahoo輸入法作為我的中文輸入的工具


若我要從中文切換為英文的話,按下caps lock,即可輸入英文


但是此時會是小寫的英文,若要輸入大寫英文,只要按 shfit + 你要打的英文即可 (前提是你已經按下了 caps lock)


2016/10/04

[Java Mail] com.sun.mail.smtp.SMTPSendFailedException: 452 message processing failed: connection timeout

Problem
I am using JavaMail to send email. But I got this error message as bellows:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
DEBUG SMTP: MessagingException while sending, THROW: 
com.sun.mail.smtp.SMTPSendFailedException: 452 message processing failed: connection timeout

 at com.sun.mail.smtp.SMTPTransport.issueSendCommand(SMTPTransport.java:2203)
 at com.sun.mail.smtp.SMTPTransport.finishData(SMTPTransport.java:1981)
 at com.sun.mail.smtp.SMTPTransport.sendMessage(SMTPTransport.java:1197)
 at org.springframework.mail.javamail.JavaMailSenderImpl.doSend(JavaMailSenderImpl.java:448)
 at org.springframework.mail.javamail.JavaMailSenderImpl.send(JavaMailSenderImpl.java:345)
 at org.springframework.mail.javamail.JavaMailSenderImpl.send(JavaMailSenderImpl.java:340)
 at com.yuantalife.ecp.commons.service.EmailService.send(EmailService.java:260)


How-to
This is not program's problem. This error may resulted from SMTP server. 
It may :

  1. The ISP server’s disk system has run out of storage space, so the action had to be cancelled.
  2. Most ISPs mail servers impose a maximum number of concurrent connections that client’s mail servers can attempt to make, and they usually also have a limit on the number of messages that are sent per connection.
  3. This error can also be indicative of a problem on your own mail server. ex. out of memory.

Reference
[1] http://www.answersthatwork.com/Download_Area/ATW_Library/Networking/Network__3-SMTP_Server_Status_Codes_and_SMTP_Error_Codes.pdf

2016/10/03

[Java] Lambda Expressions Example

If I have POJO, which named Person
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package albert.practice.lambda;

import java.io.Serializable;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class Person implements Serializable{

    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    
    private String name;
    private Gender gender;
    private int age;
    private String email;
    
}

If I have a List of Person, I would like to do search based on some criteria.

Example 1: I would like to find the person who is male and age is equal or greater than 25
Using Apache Commons Collection Utils:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
private List<Person> findByApacheCommonsEx1(List<Person> dummyData) {
     return (List<Person>) CollectionUtils.select(dummyData, new Predicate() {
         @Override
         public boolean evaluate(Object object) {
              Person person = (Person) object;
              Boolean condition = person.getAge() >= 25 && Gender.MALE.equals(person.getGender());
              return condition;
         }
     });
}

Using Lambda Expression:
1
2
3
4
5
private List<Person> findByLambdaEx1(List<Person> dummyData) {
     return dummyData.stream()
                      .filter(person -> person.getAge() >= 25 && Gender.MALE.equals(person.getGender()))
                      .collect(Collectors.toList());
}


Example 2: I  would like to find the person who is equal or greater than 25, and find the first person from the collection.
Using Apache Commons Collection Utils:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
private Person findByApacheCommonsEx2(List<Person> dummyData) {
     List<Person> result = (List<Person>) CollectionUtils.select(dummyData, new Predicate() {
         @Override
         public boolean evaluate(Object object) {
              Person person = (Person) object;
              return person.getAge() >= 25;
         }
     });

     Person person = null;
     if (!result.isEmpty()) {
         person = result.get(0);
     }
     return person;
}

Using Lambda Expression:
1
2
3
4
5
 private Person findByLambdaEx2(List<Person> dummyData) {
     Optional<Person> p = dummyData.stream()
                                         .filter(person -> person.getAge() >= 25).findFirst();
     return p.isPresent() ? p.get() : null;
 }


Example 3. Find List of String with criteria, throw RuntimeException if mismatch:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
private List<String> getDummyPlayers() {
 return Arrays.asList("王 柏 融", "林 智 勝", "林 泓 育", "蔣 智 賢", "高 國 慶");
}

// using Apache Commons CollectionUtils
private void findPlayer() {
    List<String> players = getDummyPlayers();
    String name = "陳 金 鋒";
    List<String> result = (List<String>) CollectionUtils.select(players, new Predicate() {
            @Override
     public boolean evaluate(Object object) {
      String player = (String) object;
      return name.equals(player);
     }
    });

    if (CollectionUtils.isEmpty(result)) {
        throw new RuntimeException("Cannot find player with name " + name);
    }
}

// Using Lambda expression
private void findPlayer_usingJDK8() {
    List<String> players = getDummyPlayers();
    String name = "陳 金 鋒";
    players.stream().filter(player -> name.equals(player)).findFirst()
                    .orElseThrow(() -> new RuntimeException("Cannot find player with name " + name));
}    


Example 4. Remove duplicate item in List of String and do sort:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
private List<String> getDummyPlayersWithDuplicateValue() {
    return Arrays.asList("王 柏 融", "林 智 勝", "林 泓 育", "王 柏 融", "林 智 勝", "蔣 智 賢", "高 國 慶");
}

// using Apache Commons CollectionUtils
private void removeDulpicatePlayerAndSort() {
    List<String> players = getDummyPlayersWithDuplicateValue();
    List<String> newList = new ArrayList<>();

    for (String player : players) {
        if (!newList.contains(player)) {
     newList.add(player);
 }
    }

    Collections.sort(newList, new Comparator<String>() {
 @Override
 public int compare(String o1, String o2) {
     return o1.compareTo(o2);
 }
    });

    newList.stream().forEach(player -> log.debug(player.toString()));
}

// Using Lambda expression
private void removeDulpicatePlayerAndSort_usingJDK8() {
    List<String> players = getDummyPlayersWithDuplicateValue();
    List<String> newList = players.stream().map(player -> player).distinct().sorted((o1, o2) -> o1.compareTo(o2))
      .collect(Collectors.toList());
    newList.stream().forEach(player -> log.debug(player.toString()));
}

Reference
[1] https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html
[2] https://dzone.com/articles/why-java-8-1