2017年8月19日 星期六

[C#] XML文件讀取、寫入方法

假日沒事在家用功,複習一些基本的東西順便記錄下來
有些太久沒用真得很容易忘記
內容參考《Professional C#6 and .Net Core 1.0》
================================================
XML文件

<?xml version='1.0'?>
<!-- This file represents a fragment of a book store inventory database -->
<bookstore>
  <book genre="autobiography" publicationdate="1991" ISBN="1-861003-11-0">
    <title>The Autobiography of Benjamin Franklin</title>
    <author>
      <first-name>Benjamin</first-name>
      <last-name>Franklin</last-name>
    </author>
    <price>8.99</price>
  </book>
  <book genre="novel" publicationdate="1967" ISBN="0-201-63361-2">
    <title>The Confidence Man</title>
    <author>
      <first-name>Herman</first-name>
      <last-name>Melville</last-name>
    </author>
    <price>11.99</price>
  </book>
  <book genre="philosophy" publicationdate="1991" ISBN="1-861001-57-6">
    <title>The Gorgias</title>
    <author>
      <name>Plato</name>
    </author>
    <price>9.99</price>
  </book>
</bookstore>
================================================
程式碼

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Xml;

namespace XmlExample
{
    class Program
    {
        static void Main(string[] args)
        {
            var doc = new XmlDocument();

            // 讀取xml
            using (FileStream stream = File.OpenRead("books.xml"))
            {
                doc.Load(stream);

                XmlNodeList priceNodes = doc.GetElementsByTagName("price");

                foreach (XmlNode node in priceNodes)
                {
                    // 修改內容
                    node.InnerText = "100";
                }
            }

            // 寫入xml
            var settings = new XmlWriterSettings
            {
                Indent = true,
                IndentChars = "\t",
                NewLineChars = Environment.NewLine
            };
            using (StreamWriter streamWriter = File.CreateText("newbooks.xml"))
            using (XmlWriter writer = XmlWriter.Create(streamWriter, settings))
            {
                doc.WriteContentTo(writer);
            }

            Console.WriteLine("Finish");
            Console.ReadLine();
        }
    }
}

[C#]等候異步任務回傳值

每次都忘記有Result可以用,一個簡單的紀錄



2017年4月24日 星期一

[股票投組]動能投組

股票投組對我來說是個半被動的交易方法,一開始選定一些概念後從此就不再管他,讓他自己跑自己的,就像ETF一樣,之後需要決定的只是「要不要使用這個投組」,就跟要不要使用這個ETF一樣,而不是「想辦法修改這個投組」。

以下分享一個現在自己有在使用的投組:動能投組。

這個投組組成元素有:
1. 通道突破(5週)
2. 篩選EPS>0的股票
3. 做漲最多的股票
4. 一次持有5檔

也就是說我只持有那個階段最會漲的股票,當他突破5週高點就買進,而任何一支股票跌破5週低點我就換股,換的股就是突破5週高點且最近漲最多的。

一個不知道以後有沒有辦法達成的回測績效:


可以看到10年漲了15倍,相對期貨策略來說比較少且DD也比較大,但似乎比較有可能達成。

這個投組實際上在做的時候會有一些問題:
1. 沒辦法確實將資金分為5等份,除非購買零股
2. 買最近很會漲的股票有可能開盤直接開在漲停根本買不到
3. 這個投組DD有到40%,資金管理很重要

另外一個有篩選過成交量的投組回測,績效下降但更接近真實情況:


2017年4月21日 星期五

Multicharts陣列排序函數

繼續之前的股票投組架構

Manager是一個決定誰要進場的管理員,例如Signal的條件是「破5週高點」就進場,篩選出來的股票池可能有5檔,但是資金有限或是不想一次擁有太多股票等等原因,我如果只想要進兩檔的話就需要有Manager來管理。而要決定這5檔到底最後誰要進場,可以用下方函數最排序。

函數名:StockPortfolio_Rank


用以下範例解釋此函數用法

股票排序範例



由範例可以知道在排序前StockArray是由代號依序組成的陣列:[1101,1102,1103,1104,1105],排序基準則存在另外一個ValueArray陣列裡面:[5,10,9,3,6],而排序後StockArray陣列改為由每檔股票相對應的Value依序組成:[1102,1103,1105,1101,1104],這時候如果想要進兩檔股票,則選擇StockArray[0],StockArray[1]就可以了

2017年4月14日 星期五

Multicharts實現股票投組的方法

股票投組(程式交易)決定哪檔股票進場可分為以下幾階段

階段A:篩選股票池
階段B:由股票池中選出進場標的,一般會要決定用哪個參數(EX:動能)當做排序條件,越大的越先進場

Multicharts的pmm指令可以在Portfolio Trader中讓變數互傳
藉由pmm指令達成股票投組回測具體做法可以是

1. 建立三個投組
2. 投組一(計算):專門計算進出場點、排名需要的值,傳給投組二
3. 投組二(管理):接收投組一的值,決定最後進場者,傳給投組三
4. 投組三(執行):最後執行進出場者


下次再來講程式碼...

2017年3月20日 星期一

[pmm指令] 全域變數

[前言]

Multicharts的Portfolio Trader現在有一些指令可以做投組的資金管理、變數互傳,以往的ADE只能做變數單向傳送,而現在的投組指令(因為都是pmm開頭以後就叫他pmm指令好了),可以做雙向傳送,也就是策略A傳給策略B,計算完後再傳給策略A,最近要做一些選股的模型,也把常用到的pmm指令寫在這當做給自己的一個複習。

[Pmm指令]

pmm_set_global_named_num:設定全域變數。
pmm_get_global_named_num:取得全域變數。

[觀念解釋]

<全域變數>

通常pmm_set_global_named_num、pmm_get_global_named_num是會一起使用的,只單獨使用一個沒有任何意義。全域變數的意思就是這個投組在跑的時後,這個變數可以被所有策略呼叫,也可以被所有策略修改,只要使用pmm_set_global_named_num("A",xxx),就是把全域變數「A」設定為xxx。使用pmm_get_global_named_num(A)則是取得全域變數的值。

[範利]



Test1程式碼:
========================================
pmm_set_global_named_num("GlobalVar", C);
========================================

Test2程式碼:
========================================
value1=pmm_get_global_named_num("GlobalVar");
Print(datestr(d)," TWSE:",value1," TXF:",C);
========================================

輸出結果:



2017年3月10日 星期五

閒聊2017.03.11

最近異想天開的想用機器學習篩選進場點位,我的想法是這樣:

Step1. 先用一個簡單的策略做進出場,並記錄當下特徵狀態

這邊說的特徵其實就是可以量化的各種東西,
RSI數值、Put Call Ratio、公債殖利率...等都可以拿來當做特徵

這樣整理後會出現類似下表


Step2. 將獲利做為分類目標,嘗試是否有特徵可良好完成分類

也就是看看是不是在某些特定情況下,該策略會有較良好的獲利
如果有的話,以後這些特定情況出現時,我也會較有信心進場

以上圖來說,RSI數值似乎可以當做一個不錯的特徵,
我可以選定以後RSI大於20的話我的口數進場兩倍

但真實世界沒這麼簡單,目前隨便嘗試一下,分類正確率都跟射飛鏢一樣。
這個結果也可能是我沒有真正學習過機器學習的關係,目前就是會使用一些現成套件,並把資料倒進去看看效果如何,看來要真正使用機器學習還是要先了解一下背後的原理才行。

關於套件

我使用的是python的sklearn,下圖是sklearn官網上用svm分類的例子



可以看出真得非常簡單使用,不到五行就可以用聽起來超厲害的SVM(Support vector machine;支持向量機)做完分類

2017年3月3日 星期五

[策略驗證] 週選擇權樂透策略 2013~2016

[閒聊]

之前的週選擇權樂透策略只有測試2015年Q4,今天把2013年到2016年的測試做完。QUANT TRADE是由幾個好朋友組成的交易團隊,會想寫這個網誌一部份原因是強迫我們每個禮拜都能研究一點新東西。有時後一些策略結果不盡如人意,或是更新速度比較慢,就請大家多包涵了。

[策略]

利用週選擇權損益文件,回測波動率均值回歸特性。
MC程式碼:

var:Strike_Call(0),Strike_Put(0);

Strike_Call=Floor(C/100)*100+100;
Strike_Put=Floor(C/100)*100;

if highest(h,5)-lowest(l,5)<200 then begin
print(NumToStr(d+19000000,0),",","C",",",NumToStr(Strike_Call,0));
print(NumToStr(d+19000000,0),",","P",",",NumToStr(Strike_Put,0));
end;


[結果]


總損益點數:-761
下禮拜再來測試有沒有改進方法。

2017年2月27日 星期一

買方週選擇權損益文件(2013~2016)

[說明]

週選擇權勒透策略1的Result.txt僅提供2015年Q4做為範例,這邊更新2013~2016年全部週選的買方結算損益文件。

[檔案位置]

https://drive.google.com/open?id=0BwqcpF06v4J4YzBMMEZEWEpfRmc

[注意事項]

本文件只適用於買方策略,因為買進價用的是「最後最佳賣價」,所以要做賣方的話並不是把文件上的進出場點顛倒過來就可以了。

2017年2月11日 星期六

操盤人學C# (8) 週選擇權樂透策略 final

[前言]

這個主題寫了快一個月,今天把他做一個總結囉。

[程式使用]

1. 將「操盤人學C# (5) 週選擇樂透策略1」製作出來的「Result.txt」放到D槽底下。也可到https://drive.google.com/open?id=0BwqcpF06v4J4a3UwZk9GNUNQM1U直接下載。
2. 將「操盤人學C# (6) 週選擇樂透策略2」本來在Multicharts輸出視窗中的文字,貼到新的文字檔,檔名設為「BuyPoint.txt」,放到D槽底下。也可到https://drive.google.com/open?id=0BwqcpF06v4J4TjFoWVU5YTVsZjg直接下載。
3. 執行下方程式碼。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO; //用到檔案讀寫功能

namespace WeeklyOptionLottery3
{
    class Program
    {
        static void Main(string[] args)
        {
            //先把Result.txt的全部資料存進記憶體
            List<string[]> ResultList = new List<string[]>(); //這是一個內容是文字陣列的串列

            StreamReader sr_Result = new StreamReader(@"D:\Result.txt", Encoding.Default); //讀取Result.txt用的讀取器
            sr_Result.ReadLine(); //第一行標題不要讀,BuyPoint.txt沒有標題所以不用寫這行

            //把每一行分割後存進ResultList裡面
            while (!sr_Result.EndOfStream)
            {
                string line = sr_Result.ReadLine();
                string[] results = line.Split(',');
                ResultList.Add(results); //把買賣點內容塞進去
            }
            sr_Result.Close(); //關閉讀取器

            StreamReader sr_BuyPoint = new StreamReader(@"D:\BuyPoint.txt", Encoding.Default); //讀取BuyPoint.txt用的讀取器

            double sum_profit = 0; //儲存損益的變數

            while (!sr_BuyPoint.EndOfStream)
            {
                // 這邊處理買賣點資訊
                string line = sr_BuyPoint.ReadLine();
                string[] buypoint_array = line.Split(',');
                string buy_date = buypoint_array[0];
                string buy_corp = buypoint_array[1];
                string buy_strike = buypoint_array[2];

                // 比對買賣點資訊、損益文件
                foreach (string[] result in ResultList)
                {
                    // 處理損益文件
                    string pnl_date = result[0];
                    string pnl_month = result[1];
                    string pnl_strike = result[2];
                    string pnl_corp = result[3];
                    string pnl_entryprice = result[4];
                    string pnl_endPrice = result[5];
                    if (buy_date == pnl_date && buy_corp == pnl_corp && buy_strike == pnl_strike)
                    {
                        double pnl = Convert.ToDouble(pnl_endPrice) - Convert.ToDouble(pnl_entryprice);
                        sum_profit = sum_profit + pnl;
                        Console.WriteLine("日期:" + pnl_date + " 合約:" + pnl_month + " 履約價:" + pnl_strike + " 買賣權:" + pnl_corp);
                        Console.WriteLine("進場價:" + pnl_entryprice + " 出場價:" + pnl_endPrice + " 損益:" + pnl);
                        Console.WriteLine("============"); 

                    }
                }
            }
            sr_BuyPoint.Close();
            Console.WriteLine("總損益點數:" + sum_profit.ToString());
            Console.ReadLine();
        }
    }
}
4. 輸出結果截圖


[觀念解釋]

<其實就是一堆檔案處理>

這次的內容其實沒有任何新觀念,就是一堆的檔案處理,主要是如何把「BuyPoint.txt」結合「Result.txt」。在這邊使用的方法是先將買賣點全部存到記憶體(ResultList)中,再把BuyPoint一行一行讀出來,每一行都比對全部的ResultList內容,試圖找到日期、合約規格相同的內容。

比對內容的程式碼:
if (buy_date == pnl_date && buy_corp == pnl_corp && buy_strike == pnl_strike)

[策略結果]

由結果看起來「選擇權樂透策略」在2015年第四季是可以用的,不算手續費總共獲利263.9點,不過這幾乎要歸功於2015/12/31買到2016w1的8300Put,這種獲利分配還真得很像樂透,幾次的歸零換取一次的大幅度獲利。至於其他時間這個策略獲利表現如何就留給大家自行研究了。如果有研究出來願意分享的歡迎在本網誌下方留言。

2017年1月29日 星期日

操盤人學C# (7) foreach

[前言]

foreach是一個迴圈語法,在操作大量同性質資料時非常好用,他跟for極為類似,for的用法大家可以參考Multicharts教學的書,這邊介紹foreach的性質讓大家了解,算是為下篇文章做準備吧。

[觀念解釋]

foreach主要使用情況是你要對一個陣列(串列)裡面的所有元素做「相同操作」時會用到,假設我現在要對三檔股票下單,並且對這三檔股票都要做查價、送單、回報的動作,一個方法是土法煉鋼如下表示:


輸出入下:

另一個方法是先把三檔股票加進一個股票清單,並使用foreach對股票清單每一檔股票做這三個動作:


這個就是foreach迴圈的基本寫法
string 代表StockList裡面的元素是字串型態,而stock是自定義變數名稱,stock在每跑一次迴圈時就會變成StockList裡面的另一個元素,以上面例子來說,第一次進入迴圈時stock是"台積電",第二次進入迴圈時stock是"鴻海",第三次進入迴圈時stock是"大立光"。

[衍生提示]

這邊在把股票加入StockList的時候是用Add一筆一筆加,其實更多時候Add是會寫在迴圈裡面,一次做多筆加入的動作。

2017年1月20日 星期五

操盤人學C# (6) 週選擇權樂透策略2

[前言]

延續上次課程,這次講如何用Multicharts產生週選擇權買賣文件

[觀念解釋]

<波動率>

波動率在學術上有所謂「均值回歸」的特性,而翻譯成白話就是「盤久必噴、噴久必盤」。而要操作波動率可以用選擇權的跨式價差、勒式價差來操作,關於操作概念網路上可以找到很多資料這邊就不在詳述。

<週選擇權策略-運用波動率>

假設我現在要選擇一個點進週選擇權,並且我認為盤久必噴,我訂了一個策略「台指期五日高點-五日低點<200點,則用最近期週選擇權買進價平附近履約價間隔100點的勒式價差,放到結算」,系列課程(5)我們已經建立每日週選放到結算的進出場點位表,用那個表則可以算出損益,我們只剩下要知道我們哪天進場、做哪個履約價就可以了。

上述策略在Multicharts裡面可以這樣寫:



再次說明,在這個策略中Multicharts的功用在於幫我們「產生買賣點文件」。
而輸出結果會如下方:


有了系列課程(5)的文件,我們就可以比對每天產生訊號時的損益了。


[衍生提示]

如上面方法一筆一筆用眼睛對進場點跟損益,眼睛一定找到脫窗,下次繼續介紹如何用程式把兩個文件結合起來快速產生回測結果。

2017年1月14日 星期六

操盤人學C# (5) 週選擇權樂透策略1

[前言]

所謂週選擇權樂透策略,是當你對未來行情有看法,花小錢買一個週選擇權放到到期,賭他可以一次漲個好幾倍的策略,因為以小搏大的性質跟樂透很像,所以才會有買樂透不如買週選的說法。
選擇權策略的回測用Multicharts不容易完成,如果搭配C#做一些檔案處理,會發現容易許多,本文即教大家實作好用的回測輔助文件。

[程式使用]

1. 將「2015_4_opt.csv」放進D槽,這個文件是由期貨交易所「選擇權每日行情下載」下載之2015年第四季檔案。
        下載位置:https://drive.google.com/open?id=0BwqcpF06v4J4dEpBZVhVY01XRkE
原始資料位置:https://www.taifex.com.tw/chinese/3/3_2_3.asp
2. 將「結算價.txt」放進D槽,這個文件是由期貨交易所抓取。
        下載位置:https://drive.google.com/open?id=0BwqcpF06v4J4TW45ZmQwVGtUUFU
原始資料位置:https://www.taifex.com.tw/chinese/5/FutIndxFSP.asp
3. 執行下方程式


4. 結果「Result.txt」輸出至D槽


[觀念解釋]

<樂透策略開發>

本策略只做選擇權買方,所以不會有價格被拉出去大賠問題,頂多就是選擇權價值歸零,所以在回測的時候可以想像成,我要在某個日期買進某履約價的選擇權,接下來就不再管他放至到期結算,並比較買進價格跟結算價格即是最終損益數字。
基於上述觀念,我如果有一個文件,裡面有日期、買進價格、結算價格,那我就可以知道我在那天進場買進這個樂透放至到期損益到底是多少(我們這次產生的Result.txt就是這個文件)。至於實際上那天有沒有進場,則可以用Multicharts寫一個策略,產生應該有進場的日期,兩個文件對照就可以產生樂透策略回測結果。

<Dictionary物件>

Dictionary是一個「字典」物件,可以把值加進這個字典,並在加進去的時候給他一個索引方便取出
Dictionary<string, int> result = new Dictionary<string, int>();
這行是建立一個叫做「result」的字典
result.Add(ary[1], Convert.ToInt16(ary[2]));
這行把「契約月份」當索引;「結算價」當值,
這樣以後我要取得某一個契約月份的結算價,我不用一個一個找,可以使用如result["2015W2"]的方式取出。

<選擇權買進價格>

QT選擇期交所提供的「最後最佳賣價」當作買進價格而不是選擇「收盤價」

<選擇權結算價值>

還記得選擇權結算價值怎麼算嗎?忘記快Google「選擇權內含價值」,
除了這個地方以外其他就是一些瑣碎的處理了。

[衍生提示]

在這個策略中,Multicharts的功用在於產生買賣訊號文字檔,下次會介紹如何搭配使用。
當有個這兩個文字檔,建立一個小程式讀取這兩個檔案,即可以計算回測結果,如果對C#不熟,用EXCEL的「VLOOKUP」等函數也可以辦到。
如果不太明白程式運作,Visual Studio有好用的Debug功能,可以一步一步看變數的變化,GOOGLE一下怎麼設斷點跟逐步執行吧,千萬要知道這個功能如何使用。

[工商時間]

QT會盡量在網站上寫上大部分程式碼及觀念,如果還是有不懂,可以參考實體課程
http://quanttrade168.blogspot.tw/2016/12/cmulticharts.html