kilroy 發表於 14-3-13 16:54

[分享] 用AB踏入外期程式交易

本帖最後由 kilroy 於 14-3-13 18:54 編輯

Hi,

相信很多大大應該都會覺得台指期在程式交易上的油水沒這麼好撈的感覺了?!

或是覺得程式交易怎麼前面測都覺得績效很好,近幾年似乎都走下坡

無論是懷疑自己的策略是否失效,或是覺得程式交易根本只是在做"樣本內"的事情

"樣本外"不再像歷史資料跑起來這麼"美好"

---
小弟這邊是要分享一個入門海外期貨的台階


早期接觸程式交易,我覺得最大的可惜之處就是在於視野只侷限於台指期上

時間一樣過去,但沒行情的時候,你只得勒緊褲子,等待機會

那是很煎熬沒錯,破 mdd 也是如此

如果大大你是才要開始,或是已經開始

強烈建議可以直接跨跳至海外期貨的商品,如果覺得門檻太高

建議你的策略是建立在以多個商品下,都能有較好期望值結果的方向

原因很簡單,你不希望是單一一個商品才讓你的策略進場邏輯能獲利

因為當這個商品不再是讓你的邏輯能獲利時,淨值往下掉是必然的

---
那怎麼開始你的外期程式交易呢?

1. 平台 (小弟這邊主要是介紹 AmiBroker)

   優點是程式很小、執行速度很快,一個 license 可以多台電腦執行

   可以用掃描的方式來達到不開圖表就能執行程式交易(但小弟後來不這樣做,原因等等說明)

   缺點是語法較不親切,沒有提供倉位判斷的函數,很多都要自己搞定

   與 MC 來做比較是,我覺得MC比較麻煩的地方是策略要編譯,指標和策略是分開的

   AB 這個部分可以把指標和策略合併在一起執行,也就是可以同一個策略檔案

   同時做到指標與策略進出場的圖表顯示,如此,能做到當根K進出場(如同IOG)

   對程式交易來說是很有利的

   當然,AB也不是都只有優點集於一身

   最主要還是要看大大的需求在哪裡,我只是認為 AB 有我想要的功能、能做到我想要的功能,如此而已

   如果 MC 對你而言也是如此,那用 MC 也是再自然不過的事情,你說是吧

2. 資料源(即時資料、歷史資料、資料維護)

   資料源的部分,我把它分成三個項目 "即時資料、歷史資料、資料維護"


   這個不管在哪個平台上都是需要去完成的階段

   免費的、或是本身有提供的報價源,這些就不說了 (小弟的一些心得請參考此帖)

   我主推 eSignal (相關訂閱方式以及在 AB 上如何設定與 eSignal 連線請參考此帖)

   費用上,或許會覺得有點偏高,但其實在於報價連線品質、穩定度、速度上

   絕對值得這個費用,嚴格來說還算便宜了

   且,在 AB 執行上,AB會將已經載入的資料存在本地端

   與最新的即時資料與歷史資料做連接,這個部分會自動回補 (所以速度就快啦)

   也可以做手動強制回補

   再來就是資料的維護,畢竟是花錢買的資料

   eSignal 對歷史資料的維護也做得很好,所以當你商品一多時

   也不用擔心資料維護的問題

   再者,連續月份報價對程式交易來說,是相當重要的事情

   eSignal 提供連續月份的報價 symbol

   不需要使用像 MC 的 QM 來做各月份合約的串連方式來做連續月份的 symbol

   這個部分也是相當方便的

3. 策略開發(回測、實際下單)


   想要在 AB 做 portfolio-level backtest
   相信是很多大大有興趣了解的地方了

   如何做到同一個策略同週期同參數測 N 個商品
   小弟這邊分享一下

   先拿 MC 來說,每個商品的合約規格都不同(如最小跳動點數、跳動價值等)

   這點,在AB裡,相當好處理,可以直接寫在語法裡針對不同商品做判斷與設定

   因為 AB 給了 ticksize, pointvalue, margin 等函數可以直接設定

   以小弟目前操作的商品的設定為例,以下皆以eSignal 連續月份 symbol 舉例

_SECTION_BEGIN("Product Settings");
SetPositionSize(1, spsShares);
SetOption("MaxOpenPositions", 15);
SetOption("InitialEquity", 200000);
SetOption("FuturesMode", 1);
SetOption("CommissionMode", 3);
SetOption("CommissionAmount", 2.5);
RoundLotSize = 1;

if(Name() == "6A #F")   {TickSize = 0.0001; MarginDeposit = 3000;PS = 10000; PointValue = 10*PS;   }
if(Name() == "6E #F")   {TickSize = 0.0001; MarginDeposit = 3000;PS = 10000; PointValue = 12.5*PS; }
if(Name() == "6J #F")   {TickSize = 0.0001; MarginDeposit = 4000;PS = 10000; PointValue = 12.5*PS; }
if(Name() == "6S #F")   {TickSize = 0.0001; MarginDeposit = 3000;PS = 10000; PointValue = 12.5*PS; }
if(Name() == "CL #F=1") {TickSize = 0.01;   MarginDeposit = 5000;PS = 100;   PointValue = 10*PS;   }
if(Name() == "GC #F=1") {TickSize = 0.1;    MarginDeposit = 9000;PS = 10;    PointValue = 10*PS;   }
if(Name() == "HG #F=1") {TickSize = 0.0005; MarginDeposit = 4000;PS = 1000;PointValue = 12.5*PS; }
if(Name() == "HO #F=1") {TickSize = 0.0001; MarginDeposit = 5000;PS = 10000; PointValue = 4.2*PS;}
if(Name() == "NG #F=1") {TickSize = 0.001;MarginDeposit = 10000; PS = 1000;PointValue = 10*PS;   }
if(Name() == "SI #F=1") {TickSize = 0.005;MarginDeposit = 12000; PS = 1000;PointValue = 5*PS;    }
if(Name() == "ES #F")   {TickSize = 0.25;   MarginDeposit = 4000;PS = 100;   PointValue = 0.5*PS;}
if(Name() == "YM #F")   {TickSize = 1;      MarginDeposit = 4000;PS = 1;   PointValue = 5*PS;    }
if(Name() == "ZC #F")   {TickSize = 0.25;   MarginDeposit = 2500;PS = 100;   PointValue = 0.5*PS;}
if(Name() == "ZS #F")   {TickSize = 0.25;   MarginDeposit = 4000;PS = 100;   PointValue = 0.5*PS;}
if(Name() == "ZW #F")   {TickSize = 0.25;   MarginDeposit = 3000;PS = 100;   PointValue = 0.5*PS;}
_SECTION_END();


各商品的保證金請參考此網址小弟都是只接取整數,會用到這個部分的計算跟我PZ模組與資金控管計算有關

但PZ模組與資金控管計算的部分小弟保留不分享了

在 AB 裡,我們可以用 name() 這個函數來取得目前的商品 symbol

用 if 來判斷是否目前是在這個商品執行著策略

如此,就可以設定商品合約規格了,是不是很簡單 XD

上面的商品因為都是 USD ,相對好計算,但如果是其他幣別的商品,可以自行換算成同一貨幣計算

想跑 portfolio backtest 的大大,直接把上述範例貼到你的AFL最前面就可以了

有關歷史資料的部分,請先從此帖下載後匯入 AB

---
這邊分享一下小技巧,我們可以把要跑測的商品全部加入"我的最愛"裡


將商品全部選取,然後按滑鼠右鍵選擇 "add to favorites"

開啟 new analysis 視窗,選好 *Filter 後,在旁邊的小漏斗點選進去之後,將 favorites 打勾


如此,就完成設定了,可以一次測 N 個在 favorites 裡的商品囉


---
下單的部分,AB 有提供與 IB TWS 銜接的下單軟體 (請至此下載安裝 IB Controller)

此下單軟體提供了監控的功能

比如說新的 order 送出(pending) 之後的成交回報

成交或未成交都會有回報紀錄,未成交還會有錯誤訊息的紀錄

除了監控送單的部分外,還可以監控(多商品)即時部位,以及 AB 與 IB TWS 和 IB TWS 與 IB 伺服器的連線狀況



對於緊急處理的部分,可以同時取消未成交的單子、可以同時平倉手上持有的部位

作者還提供了該程式的原碼,方便自己客製化或維護

不過目前使用來看, TWS API 很久都沒有改版啦,且這些功能已經算是相當齊全

比如說你要寫到當單子送出限價單但未成交,策略可以自動刪單改單的境界,也是可以的

---
上面簡單介紹完了 AB 對應 IB TWS 的下單機之後,接下來開始介紹如何由 AB 的策略下單至 IB TWS

提到了 AB 可以透過 SCAN 方式(不開圖表),就可以完成自動下單

小弟對這功能的熱度相當發燒友阿!所以用 AutoIt 做了一個小腳本來執行這個動作


按下 SCAN 可以開始跑出 analysis 的視窗,按下 start 就開始自動掃描自動下單了


感覺很讚對吧!而且 15個商品,不間斷掃描,每次只需要 0.17 秒左右就可以完成了


但小弟後來沒有用他的原因是因為每個商品換倉的時間不同

換倉時需要修改該(IB)商品合約名稱

那如果我按下 pause 後,等於所有商品都是在停止自動下單的狀態了

所以後來改為各別商品一個圖表的方式,加上了開關按鈕
當按下 off 之後,我可以做商品合約名稱的修改,按下 on 之後就開始自動下單啦
一來方便做換倉管理,二來一商品對應一圖表也好方便監控,因為訊息都顯示在上面了


上圖(小弟把K線關閉不顯示),可以看到目前合約名稱 CLJ4-NYMEX-FUT
如果有單進出時,BuyOrderID 或 SellOrderID 會顯示下單序號以及成交回報

而 TWS Position Size 則是顯示目前在 IB 帳戶裡該商品的持有部位

如此,是不是很方便監控與管理呢

---
接下來要講的就是策略與 IB Controller 下單機的語法了

商品合約的部分,他是 string (字串) 的格式,所以我們是這樣指定
ContractMonth = "ESH4-GLOBEX-FUT";

底下為AB透過 IB Controller 下單至 IB TWS 的語法

_SECTION_BEGIN("IB Controller");
{
ibc = GetTradingInterface("IB");

BuyTrigger = LastValue(Buy);
SellTrigger = LastValue(Short);
Reset = ParamTrigger("Reset All","RESET");
PrevTN = StaticVarGet("TimeNumber"+Name());
TN = LastValue(TimeNum());
NewBar = TN != PrevTN;
StaticVarSet("TimeNumber"+Name(),TN);
BuyOrderID = StaticVarGetText("BuyOrderID"+Name());
SellOrderID = StaticVarGetText("SellOrderID"+Name());
BuyPending = ibc.IsOrderPending(BuyOrderID);
SellPending = ibc.IsOrderPending(SellOrderID);

Shares = 1;

if( NewBar )
{
   if( NOT BuyPending ) StaticVarSetText("BuyOrderID"+Name(),"");
   if( NOT SellPending ) StaticVarSetText("SellOrderID"+Name(),"");
}
if( BuyTrigger AND BuyOrderID == "" )
{
   ibc.CloseAllOpenPositions(ContractMonth);
// BuyOrderID = ibc.PlaceOrder(ContractMonth, "BUY", Shares, "MKT", 0, 0, "DAY", True);
   BuyOrderID= ibc.ModifyOrder( BuyOrderID, ContractMonth, "Buy", Shares, "MKT", 0, 0, "Day", True);

   StaticVarSetText("BuyOrderID"+Name(),BuyOrderID);
}
else if( SellTrigger AND SellOrderID == "" )
{
   ibc.CloseAllOpenPositions(ContractMonth);
// SellORderID = ibc.PlaceOrder(ContractMonth, "SELL", Shares, "MKT", 0, 0, "DAY", True);
   SellORderID = ibc.ModifyOrder( SellORderID , ContractMonth, "Sell", Shares, "MKT", 0, 0, "Day", True);

   StaticVarSetText("SellOrderID"+Name(),SellOrderID);
}
else if( Reset )
{
   StaticVarSetText("BuyOrderID"+Name(),"");
   if( BuyPending ) ibc.CancelOrder( BuyOrderID );
   StaticVarSetText("SellOrderID"+Name(),"");
   if( SellPending ) ibc.CancelOrder( SellOrderID );
// ibc.CloseAllOpenPositions();
}

LastTWSMsg = ibc.getLastError( 0 );
BuyStatus = WriteIf( BuyOrderID != "", BuyOrderID+", Status: "+ibc.GetStatus( BuyORderID, True ),"");
SellStatus= WriteIf( SellOrderID != "", SellOrderID+", Status: "+ibc.GetStatus( SellORderID, True ),"");
LastBuyTime= Nz(StaticVarGet("LastBuyTime"+Name()));
LastSellTime= Nz(StaticVarGet("LastSellTime"+Name()));

Title = Title+"\n"+
"BuyOrderID: "+BuyStatus+"\n"+
"SellOrderID: "+SellStatus+"\n"+
"TWS Position Size: "+NumToStr(ibc.GetPositionSize( ContractMonth ),1.0,False);
}


直接將此語法貼入你的策略AFL裡,就可以下單到 IB TWS 了

在 IB TWS API 下單的商品名稱規則裡,小弟這邊說明一下

比如說

GlobexESH4-GLOBEX-FUT
NymexCLJ4-NYMEX-FUT
ECBOTZS   MAY 14-ECBOT-FUT(注意,ZS與MAY之間有三個空格,這是ECBOT旗下商品的格式)


以上,參考看看了,希望對想踏入外期程式交易的大大有所幫助

kilroy 發表於 14-6-19 16:29

skyler 發表於 14-6-19 11:30 static/image/common/back.gif
感謝k大您的回覆
跟您確認一下我的理解有沒有誤



Hi,

new bar 是以 timenum() 來抓的

ex. hourly 2:00 ->2:59 都是 2:00

若當根出現BUY 訊號,則 BUY 恆為 true

而透過 scan 或 圖表,這個值一直都會是 true

那你的送單語法...

ex. if(lastvalue(buy))
    {
   ibc.PlaceOrder( Name(), "Buy", 100, "MKT", 0, 0, "Day", Transmit);
   }


這個部份就會一直去執行,所以會重覆著送單


我們必須用 StaticVarGetText 來紀錄送單出去成交後的 ID (暫存位置)


如此,當根K只會送這麼一次 BUY ORDER 了


而判斷是否為 new bar 是為了要將這個暫存的狀態清掉

請參考此網址

---
barcomplete 最主要是用在 cross(close,value) 這種進場策略

如果你的進場條件沒用到 Close 價

不用太執著這個部分

---
有成交才會有 order ID 產生

ibc.closeallopenpositions 是市價平倉沒錯

IB Controller 的下單語法與測試

建議開好 IB 後,在 paper account 上實際測試操作最快、最符合需求

---
這個部分可以用 bar replay 功能來觀察

不過 AB 沒有細部回測
---

參考看看了

manhavecoco 發表於 14-3-13 16:59

{:5_647:}{:5_647:}狠棒的文章

enochyu 發表於 14-3-13 17:18

寶貴的經驗 ~ 大推

HaoShu 發表於 14-3-13 18:10


感謝分享...................

nsmvjmsojki 發表於 14-3-13 18:48

非常感謝您的分享   {:4_161:}

thirtycm 發表於 14-3-13 19:08

我記得97還是98年時,去聽元大洪守傑的免費課程~講當沖的。
印象中他有說到:盤中如果賺50點就可以先出場。
好諷剌,現在的盤,賺5點如果不出,等一會兒就賠錢了。

IBM2012 發表於 14-3-13 19:55

本帖最後由 IBM2012 於 14-3-13 19:57 編輯

>> BuyTrigger = LastValue(Buy);
>>SellTrigger = LastValue(Short);

沒有LastValue(sell),LastValue(cover)的部分嗎? 只有非多即空反向平倉嗎?

kilroy 發表於 14-3-13 20:01

IBM2012 發表於 14-3-13 19:55 static/image/common/back.gif
>> BuyTrigger = LastValue(Buy);
>>SellTrigger = LastValue(Short);



Hi,

範例裡的作法是先將部位平掉後,進場新部位

如果大大有需要寫到 sell 或 cover

可以從範例裡增加自己的需求


參考看看了~

shan0948 發表於 14-3-13 20:25

感謝版主無私的分享人!!

yiminchen 發表於 14-3-13 21:36

感謝分享.............................

Fesimi 發表於 14-3-13 21:54

推一個先~~~很棒的文章~~~

{:4_113:}

joshsmi 發表於 14-3-13 22:13

本帖最後由 joshsmi 於 14-3-13 22:19 編輯

To set permanent contract specifications in Information window of data base you can use this one, for example.

//run SCAN:

switch ( Name() )
{
   case "6A #F":   TickSize = 0.0001; MarginDeposit = 3000;PS = 10000; PointValue = 10*PS;   break;
   case "6E #F":   TickSize = 0.0001; MarginDeposit = 3000;PS = 10000; PointValue = 12.5*PS; break;
   case "6J #F":   TickSize = 0.0001; MarginDeposit = 4000;PS = 10000; PointValue = 12.5*PS; break;
   case "6S #F":   TickSize = 0.0001; MarginDeposit = 3000;PS = 10000; PointValue = 12.5*PS; break;
   case "CL #F=1": TickSize = 0.01;   MarginDeposit = 5000;PS = 100;   PointValue = 10*PS;   break;
   case "GC #F=1": TickSize = 0.1;    MarginDeposit = 9000;PS = 10;    PointValue = 10*PS;   break;
   case "HG #F=1": TickSize = 0.0005; MarginDeposit = 4000;PS = 1000;PointValue = 12.5*PS; break;
   case "HO #F=1": TickSize = 0.0001; MarginDeposit = 5000;PS = 10000; PointValue = 4.2*PS;break;
   case "NG #F=1": TickSize = 0.001;MarginDeposit = 10000; PS = 1000;PointValue = 10*PS;   break;
   case "SI #F=1": TickSize = 0.005;MarginDeposit = 12000; PS = 1000;PointValue = 5*PS;    break;
   case "ES #F":   TickSize = 0.25;   MarginDeposit = 4000;PS = 100;   PointValue = 0.5*PS;break;
   case "YM #F":   TickSize = 1;      MarginDeposit = 4000;PS = 1;   PointValue = 5*PS;    break;
   case "ZC #F":   TickSize = 0.25;   MarginDeposit = 2500;PS = 100;   PointValue = 0.5*PS;break;
   case "ZS #F":   TickSize = 0.25;   MarginDeposit = 4000;PS = 100;   PointValue = 0.5*PS;break;
   case "ZW #F":   TickSize = 0.25;   MarginDeposit = 3000;PS = 100;   PointValue = 0.5*PS;break;
   default:      TickSize = 0;      MarginDeposit = 0;   PointValue = 1;       break;
}

wlnumber = 0; // define watchlist of above symbols (in addition move those symbols in there if they are not in there yet)

// run scan on watchlist to set permanent contract specifications in Information window of each symbol.
if ( Status( "action" ) == actionScan AND InWatchList( wlnumber ) )
{
    AB = CreateObject( "Broker.Application" );
    st = AB.Stocks( Name() );

    st.TickSize = TickSize;
    st.RoundLotSize = 1;
    st.MarginDeposit = MarginDeposit;
    st.PointValue = PointValue;   
}

Buy = 0;

SetOption( "RefreshWhenCompleted", True );

t279923 發表於 14-3-13 23:33

感謝版主無私的分享...學習中...謝謝

jodo 發表於 14-3-14 04:05

只能說~~~K大太強了~~~~~~{:4_113:}{:4_113:}{:4_113:}{:4_113:}{:4_113:}{:4_113:}{:4_113:}{:4_113:}{:4_113:}{:4_113:}

frankychan 發表於 14-3-14 23:29

請教樓主,如何做一個連續月份的 symbol? 因為我是用IB data feed, thx!
頁: [1] 2 3 4 5 6 7 8 9 10
查看完整版本: [分享] 用AB踏入外期程式交易