2014年7月26日

股市自動交易系統(半完成品) - JavaATS程式架構 - Timer Server

這篇開始,會分幾篇來解釋目前JavaATS的程式架構,
讓有興趣修改、增加功能的網友能比較容易看懂程式。

這篇就直接切入最核心的部份,Timer Server和它相對應的Script Task List。

一開始再貼一次JavaATS程式架構圖:
這篇的重點會放在藍色字標注的區塊。

程式流程:
說得再多,比不過一張流程圖清楚,直接看流程圖:


第1張流程圖是JavaATS建構子的執行流程,
過程簡單,就是建立1個Timer定時觸發,觸發呼叫特定Method。

第2張流程圖才是關鍵所在,
當Timer觸發後,會呼叫MyTimerServerEvent這個Method,
接著它會從ScriptTasks這個List中,陸續取出所有Task來檢查和執行。

檢查時,會先判斷是否有IntervalTime(Interval模式),
如果有,就進入Interval模式。
如果沒有,就進入crontab的時間字串模式。

Interval模式:
1. 先取得上次執行的時間
2. 如果沒執行過,直接執行
3. 如果上次執行時間 + 間隔時間 >= 現在時間,表示到時或超時了,直接執行

crontab時間字串模式:
1. 先取得上次執行時間
2. 上次執行時間和現在時間同分鐘,表示執行過了
3. 週末是否設定不執行,現在是否是週末
4. 檢查現在時間是否符合crontab時間字串,符合就執行

crontab時間字串設計:
在crontab時間字串模式的設計,我一開始想了很久,
到底Linux的crontab是怎麼檢查執行的,
後來想到,Linux的crontab如果超過時間了,它是不會執行的,
我因此瞭解到它的時間檢查是反過來的。

它的核心設計是用現在時間來比較crontab時間字串是否符合。
給個簡單的例子:
假設:設定10:03分要執行 (假設時間字串為10:03)

電腦會每分鐘檢查一次現在時間,
10:00分時,時間字串為 10:00,10:00和10:03字串不符,不執行
10:01分時,時間字串為 10:01,10:01和10:03字串不符,不執行
10:02分時,時間字串為 10:02,10:02和10:03字串不符,不執行
10:03分時,時間字串為 10:03,10:03和10:03字串相符,執行

因此,它只需要每次檢查時,將現在時間轉成時間字串,
然後比較現在時間的字串是否符合Task時間的字串,
就能判斷是否執行。

當然,這裡面的字串比對比較複雜,
用傳統的indexOf或strstr或substring,那會比較到發瘋,
因此這裡是用Regular Expression,
它直接可以確定字串格式,並且把格式中所有項目的資訊一次取得,
然後各個項目比較即可。

還是以上面的10:03為例:
現在時間:Regular Expression類似(10):(02)
Task時間:Regular Expression類似(10):(03)

即可直接取得
Now[0] = 10
Now[1] = 02

TaskTime[0] = 10
TaskTime[1] = 03

接著只要Now[0]和TaskTime[0]比較,Now[1]和TaskTime[1]比較,
全部Pass,就是字串符合,這個例子中,Now[1]和TaskTime[1]不一樣,所以不執行。

延伸思考:
如果有在使用Google的Google日曆,注意到Google日曆的時間設定,
你會發現它彈性非常大,它一樣可以設定單次時間行程、週期時間行程,
但神奇的是,它定義好週期時間行程後,
還可以將週期時間行程的某幾個時間行程刪除或變更,
這樣的設計,我其實覺得滿高明的,我還沒有想通它的設計原理。

時間精確度問題:
crontab時間字串的設計有個致命傷,
因為它必須用現在時間來比較Task時間字串
因此它每次的現在時間,都必須有個緩衝空間。

假設crontab時間字串的精準度設定為
意思是,電腦必須每秒取得一次現在時間,
並且要在1秒內完成所有Task的時間字串比對,
只要電腦處理超時1秒鐘,
就會造成這秒的所有Task全部沒有執行。

文字不好表達,來個例子:

假設:
Task 1 10:00:02    定義10點00分02秒執行         執行時間0.2秒
Task 2 10:00:02    定義10點00分02秒執行         執行時間2秒
Task 3 10:00:03    定義10點00分03秒執行

以這樣有3個Task為例,
在10:00:02時,
Task 1會先執行,接著Task 2會執行。

Task 2執行完後,時間應該已經是10:00:04,
Task 3就會永遠略過不執行。

這就是為什麼crontab的時間精確度只能到的原因,
因為大部分Shell程式,很少執行時間會超過1分鐘,
crontab的說明中有稍微提到一點點,
它有提到,crontab的執行時間不能過長,否則就是建議放到背景執行。

回到JavaATS,
目前設計,我並沒有設計multi-threading,
我的假設是,Task的bsh Script執行時間,應該是不會長的太誇張,
正常網路下,一般操作的Delay,應該是不會離譜到超過1分鐘以上。

2014年7月22日

股市自動交易系統(半完成品) - JavaATS設計與使用

這篇開始,會分幾篇將JavaATS進行解說,第一篇先描述設計的使用方式。

JavaATS設計:
JavaATS的核心是一個Schedule Server,可以設定多個(理論上無限多)Task,
每個Task可以各自定時,時間到時會自動被呼叫執行。
Task就是1個1個的BeanShell Script,每次時間到,都會被呼叫執行1次。
上述的JavaATS描述,其執行示意如下圖:
整個JavaATS的設定,包括:
1. JavaATS啟動時是否執行Init Script?
2. 有哪些Task需要執行?這些Task的執行時間?執行的BeanShell檔案是?
3. MySQL Server資訊設定
4. IM的帳號、密碼、傳訊對象設定
5. NDDEAgent資訊設定

這些資訊全部都紀錄在1個XML設定檔中,並在JavaATS啟動時載入。

JavaATS使用:
JavaATS啟動:
java -jar JavaATSServer.jar [config.xml]

執行畫面如下:

程式啟動後,便會載入Config的XML檔案,從XML檔案裡面取得相關設定,
之後便會根據設定的資料開始定期執行.bsh程式。

以上述畫面中,我測試的設定,JavaATS會執行2個Script,
1個是Init.bsh,另1個是DollarCostAveraging.bsh。

Init.bsh會在系統啟動後執行,
DollarCostAveraging.bsh會每分鐘執行。

Config設定檔如果沒有填,預設會讀取目前目錄下的config.xml。

Config檔案:
Config檔案是一個標準的XML檔案,只要符合XML規格,
可以用文字檔編輯,檔案格式必須是UTF-8。

XML格式畢竟不是給人看的,因此,我另外寫了一個專門設定的UI程式,叫JavaATSGUI。

在視窗(Windows/Linux皆可)下執行:
java -jar JavaATSGUI.jar

執行畫面如下:


可以透過Load將某個Config XML載入,修改後按Save儲存。
也可以直接填寫資訊後,按Save儲存成某個Config XML檔案。

以我測試中的config.xml為例,載入後截圖如下:

檔案下載:
https://www.openfoundry.org/of/projects/2518/download

目前版本MSN的相關設定和功能都還在,
在整個整理完畢上傳後,
會開始修改這個部份,將MSN功能移除,加上Pushbullet支援。

2014年7月20日

股市自動交易系統(半完成品) - NDDEAgent

開發背景:
在台灣的程式交易中,DDE可以說是無人不知無人不曉,
這鬼東西在我一開始接觸程式交易時,
對大家講的DDE...TS...資料源,根本一頭霧水,
一查資料,乖乖不得了,這鬼東西的書竟然是1994年,
要特別到圖書館借舊書才找得到,電腦書店根本沒有這東西。

用DDE+linux為關鍵字Google,想當然,什麼鬼都沒有,
有一樣疑問的人當然有,有人有發問,但答案就是......No answer.....
因此最終放棄在Linux上呼叫或連結DDE,改在Windows上開發個Agent來做。

一開始,我期望統一用Java來開發,畢竟同語言開發有一致性,好維護,
我當時比較熟的也只有Java,但不幸的,Java的DDE只有JDDE,而且似乎要收費,
因此轉而用跟Java比較相近的c#和NDDE開發。

最早,DDE Agent的開發是一回事,證券軟體的操作是另一回事,
為了自動操作證券軟體,最早我用AutoIt另外寫了一個Stock Server
AutoIt的Stock Server專門用來操作Windows軟體,
DDE Agent則專門透過DDE取得和廣播DDE資訊,
這樣的設計實在太複雜,2個TCP Server各自運作,卻還互相關聯,
因此我將證券軟體啟動的AutoIt程式加入到DDE Agent,
成為了目前版本的NDDE Agent v1.5Beta。

一些網友會問(包括最開始的我),
DDE除了用Excel之外,怎麼接收?
DDE有辦法轉成其他Protocol嗎?
除了TS外,有沒有什麼程式可以同時設定、接收多個資料源?
NDDE Agent就是答案。

NDDE Agent功能與需求:

  1. 要能夠接收DDE訊息
  2. 要能透過TCP廣播DDE訊息
  3. 要能被控制,需要能下指令給NDDE Agent
  4. 要能將DDE註冊到證券軟體
  5. 要能啟動證券軟體(寶來點金靈)
  6. 能否提供個Web介面供呼叫?


NDDE Agent與Java ATS Server架構:

NDDE Agent基本設計:
為了能達到若干需求,NDDE Agent包括下面3個Server:
NDDE Agent的Server基本設計是Telnet Server。

所謂的常駐型Server,意思是Client端連上後,就會保持連線狀態,
直到下指令斷線離開,或者任一方斷線為止。

所謂的問答式Server,意思是這個Server是和Web Server一樣,
用一問一答方式設計,每次連上後詢問,得到答案後,會自動斷線。

在NDDE Agent中,目前裡面包裹了3個TCP Server,作用如下:
Data Server:
負責廣播所有DDE訊息。
Client連上後,所有DDE訊息都會透過它收到,它沒有指令控制,
Client連上後只能被動的接收訊息,要離開,由Client自行斷線即可。

Admin Server:
負責下指令給NDDE Agent。
Client連上後,會保持登入狀態,直到Client下exit指令離開,
Client可以透過它下DDE註冊指令,
Client也可以透過它下啟動點金靈指令,
Client也可以透過它下開啟元大寶來的B2CAPI指令。

Http Admin Server:
負責下指令、詢問NDDE Agent。
這個Server是應朋友需要加上的,透過它可以用Http URL的方式下指令給NDDE Agent,
因為它模擬Http Server,因此每次詢問或下指令後,都會回應並斷線,
它可以提供DDE訊息,但只能透過詢問方式取得,沒有即時性。

NDDE Agent特色:
1. DDE轉Socket程式
2. 支援多DDE註冊
3. Data Server用non-blocking方式開發,理論上在多Client同時接收時,能盡量降低延遲時間

NDDE Agent各部份設計:


NDDE Agent整個程式只有1個.cs檔案,算是整個Java ATS裡面數一數二醜又難看的程式,
好消息是,程式算單純,而且一段一段,分得還算清楚。

程式碼說明:
Admin Server、Http Admin Server各自是一個獨立的Thread,
Admin Server和Http Admin Server都是以TcpListener實做。

Data Server就是NDDE Agent的主程式。

NDde和EWinner的B2CAPI,它們都是設計成Event Handle的形式,
因此裡面像是objEwinner_OnDataResponse、DDEOnAdvise,
就是它們各自的Event Handler function。

DDE Connect Table和DDE Item Table,為了紀錄所有註冊的DDE連線,
因此設計2個Hash Table,各自儲存所有的DDE Connect和所有的DDE Item,
新增、刪除都會紀錄在這2個Hash Table內,然後才下function去註冊和刪除DDE。

AutoIt程式操作,AutoIt本身大概可以寫上一整篇,
先前為了寫AutoIt版的Stock Server,可以說是遇到很多麻煩,
這裡只是為了啟動點金靈,用的不多,就不細說了,
程式裡面有用到的只有2個片段,用au3為關鍵字查詢,就能看到那段內容。
這2段程式功能大意是:
登入:輸入帳號密碼、按確定、等公告、按確定
登出:關閉點金靈程式、按確定

程式啟動說明:
程式啟動後,畫面如下:

它會顯示NDDEAgent的版本號碼和Admin Server、Http Admin Server、Data Server的Port。

Admin Server指令說明:
確定NDDE Agent啟動後,此時用telnet連Admin Server,如下:

此時輸入help指令,按下Enter,如下:

會看到所有指令的一覽、說明、範例。

Http Admin Server指令說明:
和Admin Server類似,但改為瀏覽器輸入,所有指令的格式都如下:
http://[Server IP]:58892/command?[指令]

例如我這邊的環境:
NDDEAgent的IP是192.168.1.12
我要呼叫help指令,瀏覽器輸入如下:
http://192.168.1.12:58892/command?help

截圖如下:

Data Server說明:
程式啟動後,telnet到58888 Port,畫面如下:

當有DDE訊息進入後,就會直接收到,例如:


DDE語法說明:
在NDDE Agent中,針對DDE註冊,需要下指令,語法如下:
add conn DDEService|DDETopic
add item DDEService|DDETopic!DDEItem
add item DDEService|DDETopic!DDEItem
add item DDEService|DDETopic!DDEItem

以點金靈要註冊「中鋼」(2002)的成交價格、開盤價格、最高價格為例,如下:
add conn EWinner|RQ
add item EWinner|RQ!2002.Price
add item EWinner|RQ!2002.Open
add item EWinner|RQ!2002.High

截圖:


要刪除,直接刪除Connect,我記得程式會自動把item統統刪掉。
remove conn EWinner|RQ

程式需求:
執行時,程式需要下面幾個.dll檔案:

  1. 元大寶來的B2CAPI dll檔
  2. AutoIt dll檔
  3. Ndde dll檔和1個xml檔案
  4. 程式使用前,要確定AutoIt的dll有正確註冊
  5. 目前點金靈自動登入的分公司選擇無效,需手動勾選「記憶帳號」,並成功登入,讓點金靈記住分公司資訊後,才能使用NDDEAgent做自動登入的功能


使用平台是選WinXP,理論上應該適用大部分Windows系統,
選WinXP的原因是,它的使用空間最小、系統資源吃得最少。

為何不用Win2K?Win98?
之前一直是Win2K,但Win2K現在證券軟體支援很差了,只能換到WinXP。
Win98,嗯......VirtualBox的支援都很差了,不用說證券軟體了。

程式下載:
講這麼多,程式在哪?程式碼在哪?
http://www.openfoundry.org/of/projects/1818

股市自動交易系統(半完成品) - 設計發想與架構

隨著台股上9000點,加上最近比較沒這麼忙,
又想起了一直想做的程式交易。

在先前,為了做程式交易,我陸續自行開發了許多程式交易的程式和元件,
但最終因為一個人人力有限無力負擔,加上回測部份一直沒有完成,
操作演算法一直沒有頭緒,因此就讓它自然荒廢了,
我想,網路力量大,把它Open Source出來,也許會有比較多人想一起來寫,
這篇,會先介紹這個未完成系統的架構與設計發想。

程式交易架構:
程式交易系統千千萬萬,但不外乎下面這幾個元件:


設計發想:
在開始設計時,我有幾個需求:

  1. 希望在Linux上進行程式交易
  2. 希望能夠有彈性,方便開發和修改交易演算法
  3. 全自動操作,免人為操作
  4. 最好有類似證券軟體的GUI可以回測


因為上述條件,我開始進行設計。

設計:
即時資料源:
在台灣,台灣的證券公司普遍提供Microsoft DDE做為即時資料源,
我不得不說,DDE這東西已經快變成骨灰了,Microsoft自己都不太維護這東西了,
證券公司是該改改了吧,
人家國外直接有IB提供完整的API,可以抓歷史資料、即時資料、下單,
靠DDE是要打混摸魚到什麼時候?
離題了.......

因為DDE是Microsoft的東西,Linux一定不支援,
加上要做到全自動,自動啟動證券軟體的開啟、登入是必要的,
因此針對Windows的DDE,我做了下面的設計:

這個設計簡單說,就是在Windows上寫個Agent,
Linux端透過Agent取得資料源的資料,
Linux端也透過Agent控制證券軟體的操作。

歷史資料:
歷史資料主要就是Parse證交所網頁的每日資料和歷史資料,
當然也可以從證券軟體中把歷史資料匯出到Excel後取出。

歷史資料的儲存,設計上是儲存到mysql,
但理論上,因為它沒有用特殊語法,因此它應該可以支援各式SQL Server。
設計如下:

下單:
在元大寶來有提供B2CAPI,可以透過寶來點金靈進行下單,
但和取得即時資料有相同的問題,它只支援Windows,不支援Linux,
因此設計上和取得即時資料相同,都要透過Windows的Agent進行操作。
設計如下:

交易演算法:
這裡的交易演算法,是我的稱呼,它其實就是俗稱的「下單機」、「自動交易程式」...等。
設計上就是JavaATS Server(ATS為Automated Trade System的簡稱)。

它的設計發想包含2部份:

  1. 它是Time Trigger的系統
  2. 演算法容易開發、維護


Time Trigger設計:
在設計時,它的定位不是高頻交易系統,也不是當沖系統
它的設計比較趨近於短線操作(精確度:分鐘),或中長線操作(精確度:天以上),
因此它並不是根據成交觸發的Event Trigger設計
它的設計是以Time Trigger的方式,定時觸發執行。

它有2種Time Trigger觸發執行,
一種是以秒為單位的定時觸發,
另一種的執行週期最短執行為每分鐘,最長可以是數天甚至數週。

為了提供彈性的定時執行計畫,我以Linux的crontab設計為發想,設計了下面這樣的語法:
*_*_*#* *:*/1

[年]_[月]_[日]#[週] [時]:[分]

語法和crontab類似,
可定義「某年」、「某月」、「某日」、「某周」、「某時」、「某分」。

和crontab一樣,它可以定義週期執行,例如:
*_*_*#* *:*/1

*/1     表示每分鐘執行

和crontab一樣,它可以定義多個特定時間點,例如:
*_*_*#* *:1,5,10,15,20,25

1,5,10,15,20,25     表示每小時的1分、5分、10分、15分、20分、25分執行

和crontab一樣,它可以用「*」定義是否mask該時間設定,例如:
2014_7_*#* 13:0

2014年7月,每天的13:00執行

這樣的設計理論上可以提供極大的彈性,規劃出任何時間的週期執行。

這個設計的缺陷是,它不支援以秒為單位的短時間觸發,
因此,我另外設計了Interval Timer Trigger,可以設定固定間隔幾秒觸發執行。

演算法設計:
在我一開始找程式交易相關資料時,注意到下面幾個是大部分人的開發系統:

  • Excel
  • HTS

Excel:
Excel因為有大量Cell和方便的圖表產生器,
最重要的是,它有方便的VBA可以開發,
因此是很多人入門的首選。

HTS:
HTS實際上裡面提供了Power Language的程式開發功能,
可以簡單的開發出交易程式,搭配HTS內提供的圖表,可以方便的回測。

再經過一段時間的摸索後,我得出一個重要結論,
演算法不好開發,等於無法使用。

想想看,我希望有個MA(移動平均)資料,卻要寫出2頁程式碼,
這應該沒人受得了,
但偏偏傳統的通用語言C/C++、Java、Python、Perl、PHP...等,
都不是針對自動交易設計,一定是難用又難受,
光想到寫個簡單的邏輯演算法,卻要Compile再Compile,
如果是C/C++,甚至還要處理new、free、delete的問題,嗯.....很痛苦.......

但矛盾的是,大部分的程式資源,又都集中在C/C++、Java...等通用語言上,
像Power Language這類語言,沒有提供的功能,你就是無法使用,
試想看看,一個開放的競技場,不限定使用的武器,
但你用Power Language,表示你只能用長矛、用盾牌、用護甲。
槍呢?對不起,沒有提供
劍呢?對不起,沒有提供

我認為在先天上就輸了,你用它可以贏過沒有裝備的人,
但對於有能力自製武器的人而言,你先天就輸了,因為你缺乏武器........

為此,當時我找了一些Open Source的Script,
加上Java ATS一開始是用Java設計,
因此後來選定了BeanShell做為開發。

BeanShell本質上就是Java,理論上所有Java SDK都可以透過BeanShell呼叫執行,
BeanShell以Script方式執行,可以很方便的在修改後直接執行,
不用經過Compile,Java不用解釋了,它不用free/delete,
這能夠很大程度的降低開發門檻。

剩下的麻煩是呼叫方式,我希望簡單的呼叫個function,
就能夠達成,例如:
BUY(0050, 1);           //買0050,1張

因此,我當時決定寫個API,就叫做StockAPI,
提供些會用到的function,能夠方便的呼叫使用,
裡面實際上就包裹了實際操作的麻煩。

針對上述的2項設計,以及前面的幾項元件,其架構如下:
訊息通知:
訊息通知不外乎就是,
下單後通知自己,已經下單,
成交了通知自己,已經成交,
有危險時通知自己,快點逃命。

訊息通知方式,不外乎有數種IM軟體,包括:

  • MSN
  • Skype
  • GTalk
  • LINE
  • What's App
  • Yahoo Messager


實際上,有提供API的,大概只有

  • MSN
  • GTalk


Line和What's App都沒有免費提供API,Yahoo Messager我沒有使用,
Skype好像有,但之前很少用,現在被Microsoft併購後,SDK是否有提供不清楚。

因此在先前,我先開發了MSN的Client,後來寫了GTalk的Client。

SMS?
SMS要花錢,原則上直接排除。

回測:
很不幸的,回測部份我並沒有找到很好的回測設計方式,
這也是這個系統遲遲無法使用的主要原因之一。

我本來期望的是,能夠類似HTS一樣,下個Draw之類的就能畫出來,
但不幸的,每種資料的資料形式差異太大,
資料的X軸、Y軸標記形式太多,
並沒有一個固定的模式可以動態產生所有想要的圖形,
因此沒有設計出來。

另一方面,回測的作法目前我也沒有概念,
是用類似replay的方式餵資料,或者載入後直接顯示在圖形上,
還沒有想好設計方式,因此回測部份目前沒有任何設計。