2016年5月24日 星期二

範例 - 排序與過濾(3D選股)

選股的兩大手段,想做機械或程式化者必看。



哪天是最高價?
跟選股沒關係,但在時間軸分析時或許用得上。

 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
lib "personal";

public class MyScript extends CustomCorePersonal
{
 public void init()
 {
  //對CustomCore_UI初始化
  CustomCore_UI_INIT();
 }

 public void endOfPeriod(int type)
 {
  //在主副腳本都執行完歷史資料時顯示最後一天的報價
  if (type == EOH)
  {
   int[] sft_arr = sort(high, 0, 10, B2S);
   int Ht = sft_arr[0];
   dbg.print("近10根的最高價在第" + Ht + "根");

   Layer layer = getLayer("K線");
   layer.drawSteadyNext();
   layer.drawStr(Ht, high.get(Ht), "H", blue, transp(red, 22), black, LEFT, 0, true);
  }
 }
}


重點在16行的排序函數,其實也就是在ScriptBasic裡的一個函數,可以回傳排序後的t,很簡單,主要是看個人怎麼運用。


股價排行榜
上一個範例是將一個動態陣列排序,而這一個是將多檔股票的收盤價排序。

 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
lib "personal";

public class MyScript extends CustomCorePersonal
{
 ArrayList<MyInnerScript> inners = new ArrayList<MyInnerScript>();

 public void init()
 {
  CustomCore_UI_INIT();

  //取得水泥股的全部符號
  String[] arr = getCateSymbols("證所分類|金融傳產類|水泥工業");
  for (int i=0; arr!=null&&i<arr.length; i++)
  {
   //建立副腳本核心
   MyInnerScript inner = new MyInnerScript();
   inners.add(inner);
   addInnerCore(inner, arr[i], null, null, 1, PERIOD_DAY, PRECISE_DAY, false);
  }
 }

 public void endOfPeriod(int type)
 {
  //在主副腳本核心都執行完歷史資料時
  if (type == EOAH)
  {
   //排序inners陣列
   ArrayList<MyInnerScript> sorted_inners = sort(inners, 0, B2S);

   //顯示排序結果
   dbg.open();
   dbg.print("價格排行");
   dbg.print("--------------");
   for (int i=0; i<sorted_inners.size(); i++)
   {
    MyInnerScript inner = sorted_inners.get(i);
    dbg.print(inner.name + " 收盤 : " + inner.close.ref(0));
   }
  }
 }
}

//需要被排序的類別必須實做Sortable介面
class MyInnerScript extends CustomCorePersonal implements Sortable
{
 public void init()
 {
  CustomCore_UI_INIT();
 }

 //Sortable需要時做的函數
 public Double getSortValue(int keyidx)
 {
  //可依keyidx決定依何變數進行排序
  return close.ref(0);
 }
}

12~19行 : 建立多檔股票的副腳本核心(InnerCore)。
28行 : 排序。
44~57行 : 需排序的類別需時做Sortable介面,即getSortValue函數。



選股範例
再來一個完整的選股範例,有選單有表格,改一下選股的條件是可以符合你的需求,完整一個全域的選股機制。

大致先講一下這個範例的選股邏輯,就只是很簡單的將價格前幾N名與量能前幾N名選出,如果價格與量能都在前N名,則加到列表中。(這只是技術性說明...沒什麼邏輯...我知道)

  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
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
lib "personal";

public class MyScript extends CustomCorePersonal implements InpEventListener
{
 HtmlPanel my_panel = new HtmlPanel(LIGHTGRAY, BLACK, false, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
 AcTable tbl = null;

 String CFG_CATE = "證所分類|金融傳產類|水泥工業";
 int CFG_PRICE_LIMIT = 50;
 int CFG_VOL_LIMIT = 50;

 ArrayList<MyInnerScript> inners = new ArrayList<MyInnerScript>();

 public void init()
 {
  dbg.open();
  CustomCore_UI_INIT();

  //製作選單
  HtmlInputHelper html = new HtmlInputHelper(LIGHTGRAY);
  html.add(new InpMenu("CFG_CATE", getSymbolCates(), getSymbolCates(), 0, this));
  html.add("價格前 : ", new InpNum("CFG_PRICE_LIMIT", 1, 2, this), "%");
  html.add("量能前 : ", new InpNum("CFG_VOL_LIMIT", 1, 2, this), "%");
  my_panel.setText(html.get());


  //製作表格
  tbl = new AcTable();
  tbl.setTableClickListener(new TableClickListener()
  {
   //處理表格點擊事件
   public void clicked(AcTableRow row)
   {
    changeView((ScriptCore)row.custom_carry);
   }
  });
  tbl.addCol("名稱", 2, ScriptBasic.CENTRAL, true);
  tbl.init();


  //將選單與表格加入左側選單
  AcPanel panel = new AcPanel(new BorderLayout());
  panel.add(my_panel, BorderLayout.NORTH);
  panel.add(tbl, BorderLayout.CENTER);
  addTabPanel("選股", panel);


  //取得選定的股票池的全部符號
  String[] arr = getCateSymbols(CFG_CATE);
  for (int i=0; arr!=null&&i<arr.length; i++)
  {
   //建立副腳本核心
   MyInnerScript inner = new MyInnerScript();
   inners.add(inner);
   addInnerCore(inner, arr[i], null, null, 1, PERIOD_DAY, PRECISE_DAY, false);
  }
 }

 //處理選單事件
 public boolean eventOccurred(InpComponent comp, Args arg)
 {
  //事件發生時重新執行腳本
  return true;
 }

 public void endOfPeriod(int type)
 {
  //在主副腳本核心都執行完歷史資料時
  if (type == EOAH)
  {
   //排序"價格"
   ArrayList<MyInnerScript> sorted_1 = sort(inners, 0, B2S);
   //排序"量能"
   ArrayList<MyInnerScript> sorted_2 = sort(inners, 1, B2S);

   //"價格"中取出前CFG_PRICE_LIMIT%
   sorted_1 = new ArrayList<MyInnerScript>(sorted_1.subList(0, (int)(sorted_1.size()*(CFG_PRICE_LIMIT/100.0))));

   //"量能"中取出前CFG_VOL_LIMIT%
   sorted_2 = new ArrayList<MyInnerScript>(sorted_2.subList(0, (int)(sorted_2.size()*(CFG_VOL_LIMIT/100.0))));

   //顯示"價格"選出結果
   dbg.print("價格前" + CFG_PRICE_LIMIT + "%");
   dbg.print("--------------");
   for (int i=0; i<sorted_1.size(); i++)
   {
    MyInnerScript inner = sorted_1.get(i);
    dbg.print(inner.name + " 收盤 : " + inner.close.ref(0));
   }
   dbg.print("");

   //顯示"量能"選出結果
   dbg.print("量能前" + CFG_VOL_LIMIT + "%");
   dbg.print("--------------");
   for (int i=0; i<sorted_2.size(); i++)
   {
    MyInnerScript inner = sorted_2.get(i);
    dbg.print(inner.name + " 量 : " + inner.vol.ref(0));
   }

   //從"價格"中保留與"量能"重疊的部分
   sorted_1.retainAll(sorted_2);

   //將最終剩下的加到表格中顯示
   for (int i=0; i<sorted_1.size(); i++)
   {
    MyInnerScript inner = sorted_1.get(i);

    AcTableRow row = tbl.newRow(inner.symbol, inner.name);
    tbl.addRow(row);

    row.setCell(0, inner.name, Color.black);
    row.custom_carry  = inner;
   }
  }
 }
}

//需要被排序的類別必須實做Sortable介面
class MyInnerScript extends CustomCorePersonal implements Sortable
{
 public void init()
 {
  CustomCore_UI_INIT();
 }

 //Sortable需要時做的函數
 public Double getSortValue(int keyidx)
 {
  //可依keyidx決定依何變數進行排序
  if (keyidx == 0)
   return close.ref(0);
  else
   return vol.ref(0);
 }
}



主要的排序與過濾動作在71~102行,排序就是區別強弱程度,過濾就是透過一些機制淘汰或保留一些股票。可以先從單一標的下手,分析單純的時間、價格,用指標、趨勢、型態等,再轉換到全域的選股、績效回測。

沒有留言:

張貼留言

本人僅以個人知識經驗分享,多所無知,難免有錯,還請見諒。