2018年7月2日 星期一

函數(_isSettlementDay):多商品版結算日判斷

這篇文章的緣由有兩個,一是這個函數能比較多的適用在不同商品的結算日判斷,二是應該還有許多人對於在函數中的參數對宣告成 Reference 型態的使用感到疑惑。

本文所分享的函數 code 原作不是我,出處來自:https://goo.gl/7Nni3p 的第三樓。
感謝 Vincez 的分享,原始 code 請自行連過去閱讀。



首先,先理解一下這個函數的參數部分。
input: lastSettle(numericRef); 我是參數1
input: settleMonths[settleMonthsSize](numericArrayRef); 我是參數2
input: settleDayOfMonth(numericSimple);
input: settleWeek(numericSimple);
input: settleDayOfWeek(numericSimple);

第一個與第二參數宣告都是 Reference ,所以在調用本函數的時候,這兩個參數必須是可以接收回傳的形態。於是,第一個參數就不能直接填數值,而必須是數值變數。第二個參數則必須是陣列,比較特別的是,如果你想說第二個參數需要接收回傳,那麼總要告訴它回傳到陣列的第幾格吧?不需要,只要把陣列的名稱給它就行了。在這裡,我覺得你先把它想像成這樣的寫法可以讓它在第二個參數,輪流丟很多個參數進去...




我們直接透過幾個情境來體會看看,這裡的參數要怎麼下吧。

◎情境一,每個月的第三個星期三結算:
SettlementDay 是函數本身的判斷回傳,當天符合結算規則就回傳 1,否則 0。
lastSettlementDay 是從函數的第一個參數所回傳的,上次的結算日期:2018/06/20。
從下圖可以看到,指標圖翹起來的那幾個K棒就是符合結算規則的(第2到第5參數決定)


◎情境二,三月、六月、九月的第2個星期五結算:
在原作者的 code 中他使用了陣列第0格的值做為判斷條件,我也是在這裡才發現 MultiCharts 的陣列可以使用第0格了?!不知道是版本的更新,還是我誤會已久?好多年,我一直以為 MultiCharts 的陣列大小宣告都必須比要使用的格數多一格,因為第0格不能使用...


 ◎情境三,五月、六月的 25號結算:
原始的 code 在這個情境發生了錯誤。

修正一下就可以了。



總結一下以上三個情境。
●需要指定只在哪幾個月才做結算,而不是每個月,在參數二的陣列從第0格依序賦值進去;每個月都要結算,讓陣列的 第0格 是 0 就可以。
●如果要指定結算月的哪一天結算,在第三個參數就填入幾號的數值。
●第四、第五個參數是一套的,用來指定在結算月的 第N個週M 做結算。參數4、5跟第三個參數有互斥性質,只要第三個參數大於0,參數4、5就失效,填什麼都沒差。

下參數的規則是:
1. 先決定哪些月份要結算
    →1.1 每個月都結算(第二個參數,陣列值為0)
    →1.2 指定特定月份才結算(第二個參數,陣列依序賦值以指定)
2.在結算月中決定哪一種日期規則
    →2.1 指定幾號結算(在第三個參數填,比如18)
    →2.2 第幾個週幾(比如第2個週三,就在第四、第五參數填 2, 3)


因為我的個人管理需要,把原函數名稱改為 _isSettlementDay,並做一些 code 修改後如下:
// Source: https://goo.gl/7Nni3p #3
// $isSettlementDay(lastSettle, settleMonths, settleDayOfMonth, settleWeek, settleDayOfWeek);
// return 0: not settlement day
// return 1: settlement day
// return lastSettle: return last settlement date


input: lastSettle(numericRef);
input: settleMonths[settleMonthsSize](numericArrayRef);
input: settleDayOfMonth(numericSimple);
input: settleWeek(numericSimple);
input: settleDayOfWeek(numericSimple);

var: x(0);
var: _dom(0);
var: _month(0);
var: _d1om(0);
var: _d1ow(0);

_dom = dayOfMonth(date);
_month = month(date);
_d1om = date - dayOfMonth(date) + 1;
_d1ow = dayOfWeek(_d1om);

_isSettlementDay = 0;

// check settlement day on this month
if (_month <> month(lastSettle)) then begin
   // escape if not in months
   if (settleMonths[0] <> 0) then begin
      for x = 0 to settleMonthsSize + 1 begin
         if (x = settleMonthsSize + 1) then #return;
         if (_month = settleMonths[x]) then break;
      end;
   end;

   // check by day of month
   if (settleDayOfMonth > 0) then begin
      //if (settleDayOfMonth >= _dom) then begin //original
      if (_dom >= settleDayOfMonth) then begin //has fixed
         _isSettlementDay = 1;
      end;
   // check by day of week
   end else if (_d1ow <= settleDayOfWeek) then begin
      if (_dom > ((settleWeek - 1) * 7) + (settleDayOfWeek - _d1ow)) then begin
         _isSettlementDay = 1;
      end;
   end else begin
      if (_dom > (settleWeek * 7) - (_d1ow - settleDayOfWeek)) then begin
         _isSettlementDay = 1;
      end;
   end;
end;

if (_isSettlementDay <> 0) then begin
   lastSettle = date;
end;



//my modify
var: barthIntraDay(0);

barthIntraDay= iff( D<>D[1], 1, barthIntraDay+1 );

_isSettlementDay= iff( _isSettlementDay[barthIntraDay-1]=1, 1, 0 );