程式交易教學

誠邀您參加全球知名外匯經紀商OANDA的自營交易(Prop Trader)

報名OANDA自營交易(Prop Trader),並通過我們的考核,您就可以使用OANDA提供的資金進行交易,獲得高達90%的交易利潤分成。



優化了挑戰塞交易規則
無最低交易天數限制等優化了挑戰賽的交易規則。

500,000美元交易資金
您可以使用最高500,000美元的資金進行交易。

豐富的交易商品
您可以交易包括外匯、黃金、原油、股票指數等多種商品。



利用EDIT物件繪製週間計算表格的方法說明

製作指定為「年」的方格

在「EDIT物件的參數定義・設定方法」中,說明了在製作新檔案時,該如何透過EDIT物件的參數來定義表格位置。

參考文章:EDIT物件的參數定義・設定方法

接下來,將針對OnInit函數中顯示表格橫向位置的X,從第1列開始依序進行定義。

第1列維持「POSX」;第2列中,則將第1列的寬度(WIDTH_TITLE)添加至POSX;第3列中,則將WIDTH_DATA添加至第2列的x[1];在第4列,則添加WIDTH_DATA至第3列的x[2]。

int x[4];

x[0] = POSX;

x[1] = x[0] + WIDTH_TITLE;

x[2] = x[1] + WIDTH_DATA;

x[3] = x[2] + WIDTH_DATA;

其次,在第1列使用EditCreate製作指定為「年」的方格。X的位置為「x[0]」,Y的位置維持「POSY」;由於是第1列,故寬度為「WIDTH_TITLE」、高度為共用的「HEIGHT」。初始值為「2020」,而結尾則使用「false」以利進行編輯。

EditCreate(“Year”, x[0], POSY, WIDTH_TITLE, HEIGHT, “2020”, false);

EditCreate參數「ability to edit」的預設為「false」,此處將變更為「true」,藉此讓此方格之外的部分無法編輯。

read_only = true, // ability to edit

在此狀態下,如欲一概刪除後續的物件,可在檔案上方的屬性內容「#property indicator_chart_window」之下,使用「#define」來定義「PREFIX」。

#define PREFIX MQLInfoString(MQL_PROGRAM_NAME) + “_”

PREFIX代表接頭辭的意義,能夠從圖表中刪除以指定文字列開頭的物件;由於每次的物件開頭都要加上PREFIX,因此可在檔案下方「Create Edit object」的參數指定之下,寫入下列公式。

name = PREFIX + name;

但是,在 EditCreate括號內的「object name」編碼中,含有阻止更改變數數值的「const」;此處將不加以替換,而是刪除「const string」中的「const」。

bool EditCreate(string name = “Edit”, // object name

如此進行編譯並設定圖表,便完成了一個可供編輯的EDIT。

EDIT

新增陽線、陰線、十字線的方格

接下來,將在寫有2020的方格旁,製作陽線、陰線、十字線的方格。陽線的X位置設定為「x[1]」、寬度為「WIDTH_DATA」、顏色為「Red」;陰線的X位置為「x[2]」、寬度為「WIDTH_DATA」、顏色為「Blue」;十字線的X位置為「x[3]」、寬度為「WIDTH_DATA」、顏色為「Yellow」。

EditCreate(“Up”, x[1], POSY, WIDTH_DATA, HEIGHT, “陽線”, true, clrRed);

EditCreate(“Down”, x[2], POSY, WIDTH_DATA, HEIGHT, “陰線”, true, clrDodgerBlue);

EditCreate(“Cross”, x[3], POSY, WIDTH_DATA, HEIGHT, “十字線”, true, clrYellow);

其次,於「Custom indicator initialization function」下方設定「Custom indicator deinit function」,寫入下列使用OnDeinit函數的公式。

void OnDeinit(const int reason)

{

if (reason == REASON_PARAMETERS || reason == REASON_RECOMPILE || reason == REASON_REMOVE)

ObjectsDeleteAll(0, PREFIX);

}

有了此公式後,一旦變更參數、進行編譯、或是從圖表中刪除指標,便會刪除所有包含此「PREFIX」的物件。如此進行編譯後,即可新增陽線、陰線、十字線的方格

陽線、陰線、十字線的方格

新增週一~週五的表格

本次將在下方新增週間表格。先新增週一~週五的5行,縱向位置設定為POSY+高度(HEIGHT)×「i+1」。

for (int i = 0; i < 5; i++) {

int y = POSY + HEIGHT * (i + 1);

製作物件的順序將從星期開始,其次為陽線、陰線、十字線的計算結果。橫向位置及寬度皆與年的方格相同,縱向位置則替換為「y」。陽線、陰線、十字線的文字內容將在其後匯入計算數據,故此處留白;文字顏色則保留預設值的白色。

EditCreate(“DOW” + (string)i, x[0], y, WIDTH_TITLE, HEIGHT, dow);

EditCreate(“Up” + (string)i, x[1], y, WIDTH_DATA, HEIGHT, “”);

EditCreate(“Down” + (string)i, x[2], y, WIDTH_DATA, HEIGHT, “”);

EditCreate(“Cross” + (string)i, x[3], y, WIDTH_DATA, HEIGHT, “”);

但是,星期的部分還需進行其他定義;屆時使用的是枚舉值「ENUM_DAY_OF_WEEK」,藉以依序顯示0(週日)~6(週六)的週間數值。

以for文體指定的「i」為0~4,若在此處加上1,便正好符合週間數量,並變更成了週間的枚舉值。使用「EnumToString」將此更改的部分變成週間名稱;週間名稱僅需開頭3個文字即可辨認,因此設定為僅顯示文字開頭的3個文字。

string dow = StringSubstr(EnumToString((ENUM_DAY_OF_WEEK)(i + 1)), 0, 3);

如此便完成了表格部分;若在編譯後設定表格,即可顯示加上週間行數的表格。

週間行數的表格

依週間計算陽線、陰線、十字線的顯示方式

此處製作的函數「Calc」,將依照週一~週五來計算陽線、陰線、十字線的數量。

首先應設定搜尋的範圍,故將讀取顯示「年」的文字;接下來,使用將文字列變更為時間的StringToTime函數,藉以改變成開始的時間。由於外匯市場於1月1日休市,因此開始時間點若設定為「1月2日00:00」便不會出錯。

string year = ObjectGetString(0, PREFIX + “Year”, OBJPROP_TEXT);

datetime timeStart = StringToTime(year + “.01.02 00:00”);

接著要使用「iBarShift」函數,藉以將時間改為顯示中的時間週期K線數量。參數方面,可指定顯示中的貨幣對與時間週期,並以下列公式定義開始搜尋的K線。

int iStart = iBarShift(NULL, 0, timeStart);

若該K線的年份比所設定的年份更小,便不會進行以上處理;若更大則會就此結束。另外,若該條列的週間為週日或週六,亦不會進行以上處理。

其次,將在for文體上定義依照條列計算陽線或陰線的序列。若為陽線就計算「up」,陰線則為「down」;當兩者皆無時,則計算「cross」。週一的數字雖為1,但在排列上應設定為0號,因此使用「-1」。如此即可個別納入數據。

int up[5] = {0, 0, 0, 0, 0}, down[5] = {0, 0, 0, 0, 0}, cross[5] = {0, 0, 0, 0, 0};

for (int i = iStart; i >= 0; i–) {

if (TimeYear(Time[i]) < (int)year) continue;

if (TimeYear(Time[i]) > (int)year) break;

int dow = TimeDayOfWeek(Time[i]);

if (dow == SUNDAY || dow == SATURDAY) continue;

if (Close[i] > Open[i]) up[dow – 1]++;

else if (Close[i] < Open[i]) down[dow - 1]++;

else cross[dow – 1]++;

}

接下來,為了將納入的數據寫入已存在的表格文字資訊中,將添加以下公式。

for (int i = 0; i < 5; i++) {

ObjectSetString(0, PREFIX + “Up” + (string)i, OBJPROP_TEXT, (string)up[i]);

ObjectSetString(0, PREFIX + “Down” + (string)i, OBJPROP_TEXT, (string)down[i]);

ObjectSetString(0, PREFIX + “Cross” + (string)i, OBJPROP_TEXT, (string)cross[i]);

}

將此Calc函數置於「OnCalculate」之下進行編譯,便會顯示陽線、陰線、十字線的條數。

陽線、陰線、十字線的條數

顯示陽線、陰線、十字線的佔比

作為補充資訊,可在條數後方顯示陽線、陰線、十字線的佔比。

將計算出的陽線、陰線、十字線數量,個別除以總線條數量以得出佔比,再乘以100使其以百分比來呈現。此處的陽線、陰線、十字線總數若非為0,便將計算結果以括號包圍,顯示至小數點以下1位數。

for (int i = 0; i < 5; i++) {

int total = up[i] + down[i] + cross[i];

string ratioUP = “-“, ratioDown = “-“, ratioCross = “-“;

if (total != 0) {

ratioUP = “(” + DoubleToString(100.0 * up[i] / total, 1) + “%)”;

ratioDown = “(” + DoubleToString(100.0 * down[i] / total, 1) + “%)”;

ratioCross = “(” + DoubleToString(100.0 * cross[i] / total, 1) + “%)”;

}

ObjectSetString(0, PREFIX + “Up” + (string)i, OBJPROP_TEXT, (string)up[i] + ratioUP);

ObjectSetString(0, PREFIX + “Down” + (string)i, OBJPROP_TEXT, (string)down[i] + ratioDown);

ObjectSetString(0, PREFIX + “Cross” + (string)i, OBJPROP_TEXT, (string)cross[i] + ratioCross);

}

添加上列編碼並進行編譯,即可在條列數的後方顯示佔比。

條列數的後方顯示佔比

另外,由於本次是以週間來計算、不需採用週以外的計算處理,因此需要新增條件,以利處理日線以下的時間週期。在第一個for文體上新增下列的if文體。

if (_Period <= PERIOD_D1) {

最後將使用「CHARTEVENT_OBJECT_ENDEDIT」,以利在編輯EDIT物件的年份方格時,運用Calc函數來反映數值。另外,當年份編輯中輸入無意義數值時,可新增回復處理動作,將讀取文字變更為整數型。若年份數值比MT4適用的最古老年份「1970」更小、抑或比目前年份更大,便會自動帶入今年的數值。

if (id == CHARTEVENT_OBJECT_ENDEDIT) {

if (sparam == PREFIX + “Year”) {

string year = ObjectGetString(0, sparam, OBJPROP_TEXT);

string text = (string)(int)year;

if ((int)year < 1970 || (int)year > Year()) text = (string)Year();

ObjectSetString(0, sparam, OBJPROP_TEXT, text);

Calc();

}

}

如此進行編譯後,一旦輸入小於 1970、或是無意義的數據,就會轉變為今年的年份。如此即是完成。

編輯EDIT物件



編輯EDIT物件

原始碼

本次製作的原始碼如以下所示。

//+——————————————————————+

//| UpDownTable_DOW.mq4 |

//| Copyright 2021, MetaQuotes Software Corp. |

//| https://www.mql5.com |

//+——————————————————————+

#property copyright “Copyright 2021, MetaQuotes Software Corp.”

#property link “https://www.mql5.com”

#property version “1.00”

#property strict

#property indicator_chart_window

#define PREFIX MQLInfoString(MQL_PROGRAM_NAME) + “_”

//— input parameters

input int POSX = 0;

input int POSY = 15;

input int WIDTH_TITLE = 80;

input int WIDTH_DATA = 150;

input int HEIGHT = 30;

//+——————————————————————+

//| Custom indicator initialization function |

//+——————————————————————+

int OnInit()

{

//— indicator buffers mapping

int x[4];

x[0] = POSX;

x[1] = x[0] + WIDTH_TITLE;

x[2] = x[1] + WIDTH_DATA;

x[3] = x[2] + WIDTH_DATA;

EditCreate(“Year”, x[0], POSY, WIDTH_TITLE, HEIGHT, “2020”, false);

EditCreate(“Up”, x[1], POSY, WIDTH_DATA, HEIGHT, “陽線”, true, clrRed);

EditCreate(“Down”, x[2], POSY, WIDTH_DATA, HEIGHT, “陰線”, true, clrDodgerBlue);

EditCreate(“Cross”, x[3], POSY, WIDTH_DATA, HEIGHT, “十字線”, true, clrYellow);

for (int i = 0; i < 5; i++) {

int y = POSY + HEIGHT * (i + 1);

string dow = StringSubstr(EnumToString((ENUM_DAY_OF_WEEK)(i + 1)), 0, 3);

EditCreate(“DOW” + (string)i, x[0], y, WIDTH_TITLE, HEIGHT, dow);

EditCreate(“Up” + (string)i, x[1], y, WIDTH_DATA, HEIGHT, “”);

EditCreate(“Down” + (string)i, x[2], y, WIDTH_DATA, HEIGHT, “”);

EditCreate(“Cross” + (string)i, x[3], y, WIDTH_DATA, HEIGHT, “”);

}

//—

return(INIT_SUCCEEDED);

}

//+——————————————————————+

//| Custom indicator deinit function |

//+——————————————————————+

void OnDeinit(const int reason)

{

if (reason == REASON_PARAMETERS || reason == REASON_RECOMPILE || reason == REASON_REMOVE)

ObjectsDeleteAll(0, PREFIX);

}

//+——————————————————————+

//| Custom indicator iteration function |

//+——————————————————————+

int OnCalculate(const int rates_total,

const int prev_calculated,

const datetime &time[],

const double &open[],

const double &high[],

const double &low[],

const double &close[],

const long &tick_volume[],

const long &volume[],

const int &spread[])

{

//—

Calc();

//— return value of prev_calculated for next call

return(rates_total);

}

//+——————————————————————+

//| ChartEvent function |

//+——————————————————————+

void OnChartEvent(const int id,

const long &lparam,

const double &dparam,

const string &sparam)

{

if (id == CHARTEVENT_OBJECT_ENDEDIT) {

if (sparam == PREFIX + “Year”) {

string year = ObjectGetString(0, sparam, OBJPROP_TEXT);

string text = (string)(int)year;

if ((int)year < 1970 || (int)year > Year()) text = (string)Year();

ObjectSetString(0, sparam, OBJPROP_TEXT, text);

Calc();

}

}

}

//+——————————————————————+

//| Calculate function |

//+——————————————————————+

void Calc()

{

string year = ObjectGetString(0, PREFIX + “Year”, OBJPROP_TEXT);

datetime timeStart = StringToTime(year + “.01.02 00:00”);

int iStart = iBarShift(NULL, 0, timeStart);

int up[5] = {0, 0, 0, 0, 0}, down[5] = {0, 0, 0, 0, 0}, cross[5] = {0, 0, 0, 0, 0};

if (_Period <= PERIOD_D1) {

for (int i = iStart; i >= 0; i–) {

if (TimeYear(Time[i]) < (int)year) continue;

if (TimeYear(Time[i]) > (int)year) break;

int dow = TimeDayOfWeek(Time[i]);

if (dow == SUNDAY || dow == SATURDAY) continue;

if (Close[i] > Open[i]) up[dow – 1]++;

else if (Close[i] < Open[i]) down[dow - 1]++;

else cross[dow – 1]++;

}

}

for (int i = 0; i < 5; i++) {

int total = up[i] + down[i] + cross[i];

string ratioUP = “-“, ratioDown = “-“, ratioCross = “-“;

if (total != 0) {

ratioUP = “(” + DoubleToString(100.0 * up[i] / total, 1) + “%)”;

ratioDown = “(” + DoubleToString(100.0 * down[i] / total, 1) + “%)”;

ratioCross = “(” + DoubleToString(100.0 * cross[i] / total, 1) + “%)”;

}

ObjectSetString(0, PREFIX + “Up” + (string)i, OBJPROP_TEXT, (string)up[i] + ratioUP);

ObjectSetString(0, PREFIX + “Down” + (string)i, OBJPROP_TEXT, (string)down[i] + ratioDown);

ObjectSetString(0, PREFIX + “Cross” + (string)i, OBJPROP_TEXT, (string)cross[i] + ratioCross);

}

}

//+——————————————————————+

//| Create Edit object |

//+——————————————————————+

bool EditCreate(string name = “Edit”, // object name

const int x = 0, // X coordinate

const int y = 0, // Y coordinate

const int width = 50, // width

const int height = 18, // height

const string text = “Text”, // text

const bool read_only = true, // ability to edit

const color clr = clrWhite, // text color

const color back_clr = clrBlack, // background color

const color border_clr = clrWhite, // border color

const long chart_ID = 0, // chart’s ID

const int sub_window = 0, // subwindow index

const string font = “メイリオ”, // font

const int font_size = 14, // font size

const ENUM_ALIGN_MODE align = ALIGN_CENTER, // alignment type

const ENUM_BASE_CORNER corner = CORNER_LEFT_UPPER, // chart corner for anchoring

const bool back = false, // in the background

const bool selection = false, // highlight to move

const bool hidden = true, // hidden in the object list

const long z_order = 0) // priority for mouse click

{

name = PREFIX + name;

//— reset the error value

ResetLastError();

//— create edit field

if(!ObjectCreate(chart_ID, name, OBJ_EDIT, sub_window, 0, 0)) {

Print(__FUNCTION__,

“: failed to create \”Edit\” object! Error code = “, GetLastError());

return(false);

}

//— set object coordinates

ObjectSetInteger(chart_ID, name, OBJPROP_XDISTANCE, x);

ObjectSetInteger(chart_ID, name, OBJPROP_YDISTANCE, y);

//— set object size

ObjectSetInteger(chart_ID, name, OBJPROP_XSIZE, width);

ObjectSetInteger(chart_ID, name, OBJPROP_YSIZE, height);

//— set the text

ObjectSetString(chart_ID, name, OBJPROP_TEXT, text);

//— set text font

ObjectSetString(chart_ID, name, OBJPROP_FONT, font);

//— set font size

ObjectSetInteger(chart_ID, name, OBJPROP_FONTSIZE, font_size);

//— set the type of text alignment in the object

ObjectSetInteger(chart_ID, name, OBJPROP_ALIGN, align);

//— enable (true) or cancel (false) read-only mode

ObjectSetInteger(chart_ID, name, OBJPROP_READONLY, read_only);

//— set the chart’s corner, relative to which object coordinates are defined

ObjectSetInteger(chart_ID, name, OBJPROP_CORNER, corner);

//— set text color

ObjectSetInteger(chart_ID, name, OBJPROP_COLOR, clr);

//— set background color

ObjectSetInteger(chart_ID, name, OBJPROP_BGCOLOR, back_clr);

//— set border color

ObjectSetInteger(chart_ID, name, OBJPROP_BORDER_COLOR, border_clr);

//— display in the foreground (false) or background (true)

ObjectSetInteger(chart_ID, name, OBJPROP_BACK, back);

//— enable (true) or disable (false) the mode of moving the label by mouse

ObjectSetInteger(chart_ID, name, OBJPROP_SELECTABLE, selection);

ObjectSetInteger(chart_ID, name, OBJPROP_SELECTED, selection);

//— hide (true) or display (false) graphical object name in the object list

ObjectSetInteger(chart_ID, name, OBJPROP_HIDDEN, hidden);

//— set the priority for receiving the event of a mouse click in the chart

ObjectSetInteger(chart_ID, name, OBJPROP_ZORDER, z_order);

//— successful execution

return(true);

}

//+——————————————————————+

將EA自動程式交易應用於外匯與差價合約交易中

EA

我們以圖文形式詳細介紹有關EA自動程式交易的基本知識,以及在MT4/MT5平台上的安裝、參數設定方法、編碼等等內容。另外,對持有OANDA帳戶的客戶,還可以免費使用我們的獨有EA與指標工具。

誠邀您參加全球知名外匯經紀商OANDA的自營交易(Prop Trader)

報名OANDA自營交易(Prop Trader),並通過我們的考核,您就可以使用OANDA提供的資金進行交易,獲得高達90%的交易利潤分成。



優化了挑戰塞交易規則
無最低交易天數限制等優化了挑戰賽的交易規則。

500,000美元交易資金
您可以使用最高500,000美元的資金進行交易。

豐富的交易商品
您可以交易包括外匯、黃金、原油、股票指數等多種商品。