最新消息

[公告2014/05/30] 如有需要將部落格中,任何一篇文章的程式碼使用在商業用途,請與我聯繫。

[公告2015/04/26] Line版的 iInfo程式與投資應用 群組已上線想加入的朋友們,請先查看 "入群須知" 再與我聯繫 Line : aminwhite5168,加入請告知身分與回答 "入群須知" 的問題。

[公告2018/04/22] 台北 Python + Excel VBA 金融資訊爬蟲課程,課程如網頁內容 金融資訊爬蟲班:台北班 Python 金融資訊爬蟲、EXCEL VBA 金融資訊爬蟲。

[公告2019/01/08] 請注意:我再次重申,部落格文章的程式碼,是要提供各位參考與學習,一旦網頁改版請自行修改,別要求東要求西要我主動修改,你們用我寫東西賺錢了、交差了,請問有分我一杯羹嗎?既然賺錢沒分我,請問有什麼理由要求我修改,如果沒能力改,就花錢來找我上課。

[公告2019/12/01] 若各位有 Excel VBA 案子開發需求,歡迎與我聯繫,可接案處理。

[公告2020/05/22] 頁面載入速度慢,起因為部分JS來源(alexgorbatchev.com)失效導致頁面載入變慢,目前已做調整,請多見諒。

2012年9月17日 星期一

使用RW讀寫Smbus device

在網路上介紹透過RW或是RU來操作SMBus的資料很少,好不容易才找到可以參考的且實用的網頁,不過說明的又很有限,所以在此完整的說明操作流程。

1.下載安裝RWEverything,開啟RW中PCI Device table列表,找Smbus controller,如Intel PCH SMbus controoler: D31:F3,將預設以1 Byte顯示方式改為 4 Byte。
2.找到SMB_BASE的位置 , 如: offset 20h~23h。


3.右鍵點選”Open IO Space”, 開啟對應的IO Space table。


4.以下即開始介紹藉由IO操作SMBus讀寫Device的方法
(1)我們先來看幾個常用的暫存器。

(2)下面分別介紹Byte read、Word read、Block read讀取資料的步驟。

Byte read:
Step1:
清除暫存器狀態:SMB_BASE + 0x00 → Host Status。
填入0xFE清除暫存器狀態,數值變化的狀態0x40 → 0xFE → 0x00 → 0x40。

Step2:
填入裝置在SMBus的位址:SMB_BASE + 0x04 → Slave Address。
如Memory Slave Address 為0xA0~0xA6,Bit0=1代表read,所以填入0xA0 + 1 = 0xA1。

Step3:
指定欲讀取裝置內資訊擺放的起點:SMB_BASE + 0x03 → HOST Command。
讀取裝置內資訊的起始位置,從頭開始讀取,這裡先填0x00。

Step4:
選擇執行的方式與啟動:SMB_BASE + 0x02 → Host Control。
填入0x48,Bit6=1代表開始執行、Bit2~4代表選擇要使用的執行方式。



Step5:
讀取資料:SMB_BASE + 0x05 → Host Data0。
返回裝置內指定位置的資訊。



Word Read:
Step1:
清除暫存器狀態:SMB_BASE + 0x00 → Host Status。
填入0xFE清除暫存器狀態,數值變化的狀態0x40 → 0xFE → 0x00 → 0x40。

Step2:
填入裝置在SMBus的位址:SMB_BASE + 0x04 → Slave Address。
如Memory Slave Address 為0xA0~0xA6,Bit0=1代表read,所以填入0xA0 + 1 = 0xA1。

Step3:
指定欲讀取裝置內資訊擺放的起點:SMB_BASE + 0x03 → HOST Command。
讀取裝置內資訊的起始位置,從頭開始讀取,這裡先填0x00。

Step4:
選擇執行的方式與啟動:SMB_BASE + 0x02 → Host Control。
填入0x4C,Bit6=1代表開始執行、Bit2~4代表選擇要使用的執行方式。
SMB_BASE + 0x00 數值變化的狀態:0x40 → 0x42。
SMB_BASE + 0x02 數值變化的狀態:0x4C → 0x0C。



Step5:
讀取資料:SMB_BASE + 0x05 → Host Data0。
返回裝置內指定第一位置的資訊。

Step6:
讀取資料:SMB_BASE + 0x06 → Host Data1。
返回裝置內指定第二位置的資訊。


Block read:
Step1:
清除暫存器狀態:SMB_BASE + 0x00 → Host Status。
填入0xFE清除暫存器狀態,數值變化的狀態0x40 → 0xFE → 0x00 → 0x40。

Step2:
填入裝置在SMBus的位址:SMB_BASE + 0x04 → Slave Address。
如Memory Slave Address 為0xA0~0xA6,Bit0=1代表read,所以填入0xA0 + 1 = 0xA1。

Step3:
指定欲讀取裝置內資訊擺放的起點:SMB_BASE + 0x03 → HOST Command。
讀取裝置內數值的起始位置,從0x01頭開始讀取。

Step4:
選擇執行的方式與啟動:SMB_BASE + 0x02 → Host Control。
填入0x54,Bit6=1代表開始執行、Bit2~4代表選擇要使用的執行方式。
SMB_BASE + 0x00 數值變化的狀態:0x40 → 0xC1。
SMB_BASE + 0x02 數值變化的狀態:0x54 → 0x14。


Step5:
取得資料的長度:SMB_BASE + 0x05 → Host Data0。
回傳數值位元組數,該值也是愈讀取裝置啟始點的數值資料。


Step6:
SMB_BASE + 0x07 → HOST_BLOCK_DB。
返回block讀取的第一個位元組,該值為Step3指定起點 + 1數值。


Step7:
取得Block read全部數值:SMB_BASE + 0x00 → Host Status。
寫0xFF清除狀態位元,表示該筆資料已取走,通知SMBus傳回下一筆資料至offset 0x07的位置。
數值變化的狀態:0xC1 → 0xFE → 0x81 → 0xC1。
最後一筆資料顯示狀態:0xC1 → 0xFE → 0x82 → 0xC2。

以此類推,重複做Step7動作直到這個block讀完。

資料來源:怎樣在RU下讀寫Smbus device

2012年9月15日 星期六

使用Internet Explorer object下載每日上櫃交易明細


抓取上櫃交易明細需要使用POST來傳遞參數,在VBA中能使用的方法就是使用XmlHttp。
不過我們想嘗試使用其他Excel VBA抓取網頁數據的方法來抓取,於是選用了Internet Explorer物件來試著抓取上櫃交易明細。

如下使用Internet Explorer物件抓取上櫃交易明細
Option Explicit
Sub ie_test_Navigate_PostData()
    Dim objIE As Object
    Dim bPostData() As Byte
    Dim xlURL As String
    Dim UrlHeaders As String

    xlURL = "http://www.gretai.org.tw/ch/stock/aftertrading/broker_trading/download_ALLCSV.php"
    UrlHeaders = "Content-Type: application/x-www-form-urlencoded" & vbCrLf
    bPostData = StrConv("stk_date=1010914&curstk=5349", vbFromUnicode)
    
    Set objIE = CreateObject("InternetExplorer.Application")
    With objIE
        .Visible = True
        .Navigate xlURL, , , bPostData, UrlHeaders
        While .ReadyState <> 4 Or .Busy = True
            DoEvents
        Wend
    End With
End Sub
雖然可以透過InternetExplorer物件來抓取上櫃交易明細,但當順利取得檔案時卻因需要點選下載檔案對話框,因此這裡可能就無法使用InternetExplorer物件抓取。


對於Navigate進階的使用可參考這裡。

VC++, JavaScript的日期函數使用

因工作要使用VC++時間處理函數,加上近期在研究Highstock也需要使用到JavaScript時間處理函數,所以將兩者處理時間的方法一起列出

以下是一個使用VC++時間處理類別CTime的範例
int main(int argc, TCHAR* argv[])
{
 CTime cTime = CTime::GetCurrentTime(); 
 printf("Year : %d\n", cTime.GetYear());
 printf("Month : %d\n", cTime.GetMonth());
 printf("Day : %d\n", cTime.GetDay());
 printf("Hour : %d\n", cTime.GetHour());
 printf("Minute : %d\n", cTime.GetMinute());
 printf("Second : %d\n", cTime.GetSecond());
 printf("Time : %d\n", cTime.GetTime());
 return 1;
}
執行結果

以下是一個使用JavaScript時間處理函數的範例
<html>
  <head>
  <title>JavaScript Test Date Function</title>
  <script type="text/javascript">
    function run() {
      var CurrentTime = new Date();
      document.write("Year : " + (CurrentTime.getYear() + 1900) + "<br>")
      document.write("Month : " + (CurrentTime.getMonth() + 1) + "<br>")
      document.write("Date : " + CurrentTime.getDate() + "<br>")
      document.write("Hours : " + CurrentTime.getHours() + "<br>")
      document.write("Minutes : " + CurrentTime.getMinutes() + "<br>") 
      document.write("Seconds : " + CurrentTime.getSeconds() + "<br>")
      document.write("Time : " + CurrentTime.getTime() + "<br>")
    }
  </script>
  </head>
  <body onload = run();>
  </body>
</html>
執行結果

兩者的差異

VC++
JavaScript
Time
Second
Millisecond
Year
不加1900
需加1900
Month
以1為起點,範圍1~12
以0為起點,範圍0~11

2012年9月8日 星期六

使用Stored Procedure取得SQLyog的版本

透過SQLyog寫一個Stored Procedure(預存程序),來取得SQLyog版本資訊
DELIMITER //
CREATE
    PROCEDURE `stock`.`GetDBVersion`(OUT VERSION VARCHAR(60),OUT DAY VARCHAR(60))
    LANGUAGE SQL NOT DETERMINISTIC
    CONTAINS SQL SQL SECURITY DEFINER
    BEGIN
        SELECT VERSION() INTO VERSION;
        SELECT CURDATE() INTO DAY;
    END//
DELIMITER ;

取得SQLyog版本資訊前先初始化GetDBVersion預存程序
CALL GetDBVersion(@version,@day);

再下SQL指令取得版本
SELECT @version,@day;

執行結果

2012年9月7日 星期五

使用Stored Procedure列出交易日

透過SQLyog寫一個Stored Procedure(預存程序),來產生一個連續日期的序列
DELIMITER $$
CREATE
    PROCEDURE `stock`.`gen_time_sequence`(startstamp TIMESTAMP, endstamp TIMESTAMP, intval INTEGER, unitval VARCHAR(10))
    BEGIN
 DECLARE thisStamp TIMESTAMP;
 DECLARE nextStamp TIMESTAMP;
 SET thisStamp = startstamp;

 -- Drop and Create the temp table

 DROP TEMPORARY TABLE IF EXISTS timestamp_sequence;

 CREATE TEMPORARY TABLE IF NOT EXISTS timestamp_sequence(
 id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,    
 TIME TIMESTAMP
 );    
  
 -- 迴圈, 每次增加間隔
 REPEAT 
  SELECT CASE unitval
   WHEN 'MICROSECOND' THEN TIMESTAMPADD(MICROSECOND, intval, thisStamp)
   WHEN 'SECOND'      THEN TIMESTAMPADD(SECOND, intval, thisStamp)
   WHEN 'MINUTE'      THEN TIMESTAMPADD(MINUTE, intval, thisStamp)
   WHEN 'HOUR'        THEN TIMESTAMPADD(HOUR, intval, thisStamp)
   WHEN 'DAY'         THEN TIMESTAMPADD(DAY, intval, thisStamp)
   WHEN 'WEEK'        THEN TIMESTAMPADD(WEEK, intval, thisStamp)
   WHEN 'MONTH'       THEN TIMESTAMPADD(MONTH, intval, thisStamp)
   WHEN 'QUARTER'     THEN TIMESTAMPADD(QUARTER, intval, thisStamp)
   WHEN 'YEAR'        THEN TIMESTAMPADD(YEAR, intval, thisStamp)
   END INTO nextStamp;
  -- 過濾週六,週日與特殊日子
  IF DAYOFWEEK(thisStamp) <> 1 AND DAYOFWEEK(thisStamp) <> 7 OR DATE(thisStamp) = STR_TO_DATE('2012-08-02', '%Y-%m-%d') THEN
   INSERT INTO timestamp_sequence(Dateid, Weekdayid) SELECT DATE(thisStamp), DAYOFWEEK(thisStamp);
  ELSEIF DATE(thisStamp) = STR_TO_DATE('2012-03-03', '%Y-%m-%d') THEN
   INSERT INTO timestamp_sequence(Dateid, Weekdayid) SELECT DATE(thisStamp), DAYOFWEEK(thisStamp);
  END IF;
  SET thisStamp = nextStamp;
 UNTIL thisStamp >= endstamp
 END REPEAT;
    END$$
DELIMITER ;

要產生時間序列前先初始化gen_time_sequence預存程序
CALL gen_time_sequence('2012-01-01','2012-12-31', 1, 'DAY');  

取得時間序列產生的天數,執行以下SQL指令
SELECT COUNT(1) FROM timestamp_sequence;

執行結果

取得時間序列中的前10筆資料
select * from timestamp_sequence limit 10;

執行結果