恥は/dev/nullへ by 初心者

プログラミング素人がのろのろと学んだことをつづっています♪

キリ番に水平線を描画してみる

タイトル通りのコードです。ラウンドナンバーになっている価格に水平線を描画します。MQLの勉強も兼ねて書いてみました。

チャート・リーディングをする時にどのラウンドナンバーが意識されているか調べることが主な使用目的です。

 

使い方

基本的にはデフォルトのまま実行して良いと思います。

デフォルトでは「0にする桁数」(SelectedDigits)が「2」なので、ダブルオーに水平線を描画します。

もし下3桁が0(つまり、000)の価格に水平線を描画したい場合、「0にする桁数」を「3」にします。

 

コード(スクリプト版)

//--------------------------------------------------------------
// FileName : DrawHLineRoundNum_Script.mq4
//
// Feature  : This script draws horizontal lines at level of 
//            round numbers.
//--------------------------------------------------------------
#property copyright "Copyright 2020. PHILOJUAN"
#property link      "https://uhoho.hatenablog.jp/"
#property version   "1.00"
#property strict
#property script_show_inputs

// ***************** パラメーター *****************
input int SelectedDigits = 2; // 0 にする桁数(下2桁を00にする場合は 2)
input int Step           = 1; // ステップ数(1 ~ 9の整数)
extern int BarsToSearch  = 0; // 検索対象とする本数(0 はチャート全体)
input color LineColor    = clrDarkTurquoise; // 水平線の色
input ENUM_LINE_STYLE LineStyle  = STYLE_SOLID; // 水平線のスタイル

//<簡単な説明>
// SelectedDigits
// 0 にする桁数。例えば、下2桁を00にする場合は 2 を入力。
//
// Step
// 下2桁を0(つまり、00)にしているとして実例を示すと・・・
// Step = 1 なら、価格の下3桁が 100 → 200 → 300 という幅で水平線を描画
// Step = 2 なら、価格の下3桁が 100 → 300 → 500 という値幅で水平線を描画
// Step = 3 なら、価格の下3桁が 100 → 400 → 700 という値幅で水平線を描画
//
// BarsToSearch
// キリ番表示範囲の上限と下限となる高値・安値を調べる時、1本前から
// 何本前までを検索対象とするかを指定する。
// 0を指定した場合は、(現在足を除く)チャート上の全てのローソク足
// が対象となる。


int OnStart()
{
    // 不正なパラメーターが指定された場合の処理 1
    if((Digits < SelectedDigits) || (SelectedDigits < 1)){
        Alert("Error : Read the message displayed on Expert!");
        printf("「0にする桁数」が不正です。この通貨ペアの場合、指定できるのは 1 ~ %d です。", Digits);
        return(INIT_PARAMETERS_INCORRECT);
    }

    // 不正なパラメーターが指定された場合の処理 2
    if(Step < 1 || Step > 9){
        Alert("Error : Read the message displayed on Expert!");
        printf("「ステップ数」が不正です。指定できるのは 1 ~ 9 です。");
        return(INIT_PARAMETERS_INCORRECT);
    }

    // 不正なパラメーターが指定された場合の処理 3
    if(BarsToSearch < 0 || (BarsToSearch > Bars - 1)){
        Alert("Error : Read the message displayed on Expert!");
        printf("「検索対象とする本数」が不正です。このチャートの場合、指定できるのは 0 ~ %d です。", Bars - 1);
        return(INIT_PARAMETERS_INCORRECT);
    }

    if(!BarsToSearch)
        BarsToSearch = Bars - 1;

    double LowestPrice  = Low[iLowest(  NULL, 0, MODE_LOW, BarsToSearch, 1)];
    double HighestPrice = High[iHighest(NULL, 0, MODE_HIGH, BarsToSearch, 1)];

    double multiplier  = MathPow(10, Digits - SelectedDigits); // 乗数
    int RoundPriceLow  = (int)(LowestPrice * multiplier);  // LowestPriceに乗数を掛けて、小数点以下を切捨て
    int RoundPriceHigh = (int)(HighestPrice * multiplier); // HighestPriceに乗数を掛けて、小数点以下を切捨て

    for(int i = RoundPriceLow, j = 0; i <= RoundPriceHigh; i += Step, j++){
        
        double DrawPrice = i / multiplier; // 小数点の位置を元に戻した値 (multiplierはdouble型)
        
        string hLineName = "uhoRoundNumLine" + IntegerToString(j);  // オブジェクト名を生成
        // [備考] 他のオブジェクト名と被らないように、変わった名称の方が無難

        if(ObjectFind(0, hLineName) < 0){     // 同じ名前のオブジェクトが無いかチェック
            ObjectCreate(    0, hLineName, OBJ_HLINE, 0, 0, DrawPrice);
            ObjectSetInteger(0, hLineName, OBJPROP_COLOR, LineColor);
            ObjectSetInteger(0, hLineName, OBJPROP_STYLE, LineStyle);
            ObjectSetInteger(0, hLineName, OBJPROP_WIDTH, 1);
            ObjectSetInteger(0, hLineName, OBJPROP_BACK, false);
            ObjectSetInteger(0, hLineName, OBJPROP_SELECTABLE, true);
            ObjectSetInteger(0, hLineName, OBJPROP_SELECTED, false);
        }
    }

    return(0); 
}

 

上述のスクリプトで描画した水平線を削除するコード

//--------------------------------------------------------------
//
// FileName : DeleteHLineRoundNum_Script.mq4
//
//--------------------------------------------------------------
#property copyright "Copyright 2020, PHILOJUAN"
#property link      "https://uhoho.hatenablog.jp/"
#property version   "1.00"
#property strict

void OnStart()
{
    int numObj = ObjectsTotal();

    for(int k = 0; k < numObj; k++){
        string hLineName = "uhoRoundNumLine" + IntegerToString(k);

        if(ObjectFind(0, hLineName) >= 0)
            ObjectDelete(0, hLineName);
    }
}

 

 

浮動小数点数を受けるtype変換指定子 on Visual Studio 2019

 [この記事はまだ書きかけです・・・]

 

以下の実験コードを書いてみました。

#include <stdio.h>

int main(void)
{
    double uho;
    scanf("%f", &uho);
    printf("%f", uho);
    
    return 0;
}

このコードをビルドしようとしたら、以下のメッセージが表示されました。

warning C4477 : 'scanf' : 書式文字列 '%f' には、型 'float *' の引数が必要ですが、可変個引数 1 は型 'double *' です
書式文字列に '%lf' を使用することをお勧めします

 

 

どうやら、scanf()においてdouble型を受け取る場合、「%lf」とする必要があるみたいです。一方、printf()においては「%f」のままでも問題無さそうです。

 

リンク先の文書を読むと、printf()において、「f」はdouble型に、「lf」はlong double型に使えるようですが、上述したコンパイラのメッセージによると、scanf()で「%f」はfloat型の引数を想定するようです。

 

この違いは何なのでしょうか???(scanf()に関する文書を調べる必要がありそうです。)

 

整数を受けるtype変換指定子 on Visual Studio 2019

type変換指定子に関する文書

Visual Studio 2019のtype変換指定子(ページ中段)
https://docs.microsoft.com/ja-jp/cpp/c-runtime-library/format-specification-syntax-printf-and-wprintf-functions?view=vs-2019

日本語の説明文がぎこちない時は「英語で読む」をONにした方がいいです^^;。

 

★%dと%iの違いに関する情報
https://www.it-swarm.dev/ja/c++/%E3%83%95%E3%82%A9%E3%83%BC%E3%83%9E%E3%83%83%E3%83%88%E3%81%95%E3%82%8C%E3%81%9Fio%E9%96%A2%E6%95%B0%E3%81%AE%E5%A4%89%E6%8F%9B%E6%8C%87%E5%AE%9A%E5%AD%90%EF%BC%85i%E3%81%A8%EF%BC%85d%E3%81%AE%E9%81%95%E3%81%84%E3%81%AF%E4%BD%95%E3%81%A7%E3%81%99%E3%81%8B%EF%BC%88-printf-scanf%EF%BC%89/968640762/

 

各データ型が取る範囲に関する文書

整数値

https://docs.microsoft.com/ja-jp/cpp/c-language/cpp-integer-limits?view=vs-2019

 

浮動小数点数

https://docs.microsoft.com/ja-jp/cpp/c-language/limits-on-floating-point-constants?view=vs-2019

 

型に応じたtype変換指定子と実験結果

実験結果を書く前に・・・、unsigned int と unsigned long int について

unsigned int       %u
unsigned long int  %lu

と某書籍に書かれていたのですが・・・、Visual Studio 2019では unsigned int と unsigned long intの最大値が同じなので、%u と %lu は実質的に同じです。

 


 

 <結果1> short int として受け取る %hd

32767 を %hd で受けると: 32767
short intは 2 バイト(-32768 ~ 32767)

 

 <結果2> %hd(short int)の範囲をオーバーした場合

32770 を %hd で受けると: -32766
short intは 2 バイト(-32768 ~ 32767)

 

<結果3> long int として受け取る %ld

2147483647 を %ld で受けると: 2147483647
long intは 4 バイト(-2147483648 ~ 2147483647)

 

<結果4> %ld(long int)の範囲をオーバーした場合

2147483650 を %ld で受けると: -2147483646
long intは 4 バイト(-2147483648 ~ 2147483647)

 

<結果5> unsigned int として受け取る %u

4294967295 を %u で受けると: 4294967295
unsigned intは 4 バイト(最大値 4294967295)

 

<結果6> %u(unsigned int)の範囲をオーバーした場合

4294967296 を %u で受けると: 0
unsigned intは 4 バイト(最大値 4294967295)

[備考] %uではなく%llu(または %I64u)を使いなさいとメッセージが出た。

 

<結果7> long long int として受け取る %lld

4294967296 を %lld で受けると: 4294967296
long long intは 8 バイト(-9223372036854775808 ~ 9223372036854775807)

 

<結果8> %lld(long long int)の範囲をオーバーした場合

9223372036854775810 を %lld で受けると: -9223372036854775806
long long intは 8 バイト(-9223372036854775808 ~ 9223372036854775807)

 

<結果9> unsigned long long int として受け取る %llu

18446744073709551615 を %llu で受けると: 18446744073709551615
unsigned long long intは 8 バイト(最大値 18446744073709551615)

[備考] 最大値は1844京以上。

 

<結果10> %llu(unsigned long long int)の範囲をオーバーした場合

unsigned long long intの最大値よりも大きい値を指定してビルド
しようとしたらビルドできず・・・。

 

上記結果の元となったコード

<コード1>

#include <stdio.h>
#include <limits.h>

int main(void)
{
    printf("32767 を %%hd で受けると: %hd\n", 32767);
    printf("short intは %d バイト(%d ~ %d)\n", sizeof(short int), SHRT_MIN, SHRT_MAX);
    return 0;
}

 

<コード2>

#include <stdio.h> 
#include <limits.h>

int main(void) { printf("32770 を %%hd で受けると: %hd\n", 32770); printf("short intは %d バイト(%d ~ %d)\n", sizeof(short int), SHRT_MIN, SHRT_MAX); return 0; }

 

<コード3>

#include <stdio.h> 
#include <limits.h>

int main(void) { printf("2147483647 を %%ld で受けると: %ld\n", 2147483647); printf("long intは %d バイト(%d ~ %d)\n", sizeof(long int), LONG_MIN, LONG_MAX); return 0; }

 

<コード4>

#include <stdio.h> 
#include <limits.h>

int main(void) { printf("2147483650 を %%ld で受けると: %ld\n", 2147483650); printf("long intは %d バイト(%d ~ %d)\n", sizeof(long int), LONG_MIN, LONG_MAX); return 0; }

 

<コード5>

#include <stdio.h> 
#include <limits.h>

int main(void) { printf("4294967295 を %%u で受けると: %u\n", 4294967295); printf("unsigned intは %d バイト(最大値 %u)\n", sizeof(unsigned int), UINT_MAX); return 0; }

 

<コード6>

#include <stdio.h> 
#include <limits.h>

int main(void) { printf("4294967296 を %%u で受けると: %u\n", 4294967296); printf("unsigned intは %d バイト(最大値 %u)", sizeof(unsigned int), UINT_MAX); return 0; }

 

<コード7>

#include <stdio.h> 
#include <limits.h>

int main(void) { printf("4294967296 を %%lld で受けると: %lld\n", 4294967296); printf("long long intは %d バイト(%lld ~ %lld)", sizeof(long long int), LLONG_MIN, LLONG_MAX); return 0; }

 

<コード8>

#include <stdio.h> 
#include <limits.h>

int main(void) { printf("9223372036854775810 を %%lld で受けると: %lld\n", 9223372036854775810); printf("long long intは %d バイト(%lld ~ %lld)", sizeof(long long int), LLONG_MIN, LLONG_MAX); return 0; }

 

<コード9>

#include <stdio.h> 
#include <limits.h>

int main(void) { printf("18446744073709551615 を %%llu で受けると: %llu\n", 18446744073709551615); printf("unsigned long long intは %d バイト(最大値 %llu)", sizeof(unsigned long long int), ULLONG_MAX); return 0; }

 

<コード10>

/* このコードはビルドできなかった */
#include <stdio.h>
#include <limits.h>
int main(void) { printf("18446744073709551620 を %%llu で受けると: %llu\n", 18446744073709551620); printf("unsigned long long intは %d バイト(最大値 %llu)", sizeof(unsigned long long int), ULLONG_MAX); return 0; }

疑問

 

以下のコードを実行すると、1.0 から 10.0 まで表示されます。

 

/* ren4-7-1.c */
#include <stdio.h>

int main(void)
{
    double d;
    for (d = 1; (int)d <= 9; d += 0.1) {
        printf("%.1lf\n", d);
    }

    return 0;
}

 

dの値が 10.0 になった時、(int)d の値は 10 になるので、10.0 は表示されないものと思っていたのですが・・・、これは何故でしょうか??

(ヒマな時に2進数で考えてみると何か分かるかな?)

 

 

continueの挙動(MQL4)

[追記:2020/08/07]

C言語での実験結果(for文中のcontinue)を本記事末尾に追記しました。


 

今更ながら初歩的なことを確認してみました。

MQLのcontinueの挙動です。

 

for文の中でcontinueを使った場合、continueより後ろに続くコードは処理されずfor文の先頭に処理が移りますよね。この時、ループ回数を制御するカウンタは回る(インクリメントまたはデクリメントされる)と思っていたのですが、「この理解で合ってるよね?」と確認したくなったのです。

 

そこで、コードを書いて実験してみました。以下の実験結果に登場する「i」はfor文のループ回数を制御しているカウンタです。そして「CONTINUE!」という行はcontinueが実行されたことを示しています。

for文の先頭 iの値は: 999
CONTINUE!
for文の先頭 iの値は: 998
CONTINUE!
for文の先頭 iの値は: 997
CONTINUE!
for文の先頭 iの値は: 996
CONTINUE!
for文の先頭 iの値は: 995

 

この結果を見ると、for文ではループ回数を制御するカウンタが回っていますね

 

では、、、while文やdo-while文ではどうなるのでしょうか?

for文と違ってインクリメント部(デクリメント部)が存在しないので、コード作成者が意図的にカウンタを回さないと無限ループに陥る気がします。

 

以下のMQLスクリプトを実行してみました。

void OnStart()
{
   int i = 1;
   do {
      printf("iの値は: %d",i);
      if(i % 2 == 0)
         continue;
      printf("奇数");
      i++;
   } while (i < 50);
}

 

実行結果

iの値は: 1
奇数
iの値は: 2
iの値は: 2
iの値は: 2
iの値は: 2
iの値は: 2
iの値は: 2
(以下略)

 

予想通り無限ループ状態になりましたコードから見て当たり前ですが)。

continueの直前でループ条件に関わる変数「i」をインクリメントしておく必要がありますね。

 

if文部分を以下のように変更します。

   if(i % 2 == 0) {
      i++;
      continue;
   }

 

 このコードを実行すると以下のログが出力されました。

iの値は: 1
奇数
iの値は: 2
iの値は: 3
奇数
iの値は: 4
iの値は: 5
奇数
iの値は: 6
iの値は: 7
奇数
iの値は: 8
iの値は: 9
奇数
iの値は: 10
iの値は: 11
奇数
(以下略)

 

これなら問題ないですね。

 

C言語ではどうなるか?(for文中のcontinue)

for文中のcontinueの挙動について、C言語でも実験をしました。

コードは以下のとおりです。

#include <stdio.h>

int main(void)
{
   int i = 1;

   for (i = 1; i <= 10; i++) {
      printf("iの値は: %d\n", i);

      if (!(i % 2)) {
         printf("CONTINUE!\n");
         continue;
      }
      printf("奇数!\n");
   }
   return 0;
}

 

コードの実行結果

iの値は: 1
奇数!
iの値は: 2
CONTINUE!
iの値は: 3
奇数!
iの値は: 4
CONTINUE!
iの値は: 5
奇数!
iの値は: 6
CONTINUE!
iの値は: 7
奇数!
iの値は: 8
CONTINUE!
iの値は: 9
奇数!
iの値は: 10
CONTINUE!

 

continueした際、ちゃんとインクリメント部が実行されている(変数「i」がインクリメントされている)ことが確認できました^^。

 

 

このelseの相手はだ~れ?

以下のインデントがおかしなコードをご覧ください。

#include <stdio.h>

int main(void)
{
    int a, b;
    a = 1;
    b = 0;

    if (a)
        if (b)
            printf("aもbも真アルよ");
else
    printf("aは真だけどbは偽アルよ");

    return 0;
}

 

printf文の中に記述した文言を読んでしまうと答えがバレバレですが、このコードにおいてelseはどちらのifに対応するのかと聞かれるとちょっと迷いますよね。

 

書籍によると、同じブロックで「どのelseとも対応していない」「最も近いif」に対応するそうです。上のコードでは if(b)に対応することになります。

 

ちなみに、コードの実行結果は以下のとおりです。

 aは真だけどbは偽アルよ

 

 

getcharとgetcheの戻り値はint型らすぃ

getcharもgetcheも文字を扱う関数ですが、書籍によると戻り値はint型と書かれています。「char型じゃないんか!」と少し驚きつつ、調べてみました。


C言語学習はVisual Studio Community 2019で行っているので、MicroSoft社のページを調べてみました。

 

 以下のリンク先を見ると、getcharの戻り値は確かにint型と書かれています。

https://docs.microsoft.com/ja-jp/cpp/c-runtime-library/reference/getchar-getwchar?view=vs-2019#syntax
 

次はgetcheの戻り値を調べてみましょう。
https://docs.microsoft.com/ja-jp/cpp/c-runtime-library/reference/getche?view=vs-2019
このリンク先に

Microsoft 固有の関数名 getche は、 _getche関数の非推奨のエイリアスです。

と書かれています。

 

そこで、_getcheのページを読んでみると、戻り値は int型と書かれています。

getche_getcheエイリアスなら、getcheの戻り値もint型になるはずです。

 

実験がてら、以下のコードを書いてみました。

#include <stdio.h> 
#include <conio.h>

int main(void)
{
    printf("getcheの戻り値は %d バイト", sizeof(getche()));
    return 0;
}

 

コードの実行結果

getcheの戻り値は 4 バイト

これはint型のサイズと一致します(備考:私の環境では、int型は4バイトです)。 

 

以上のことから、getcheの戻り値はint型の可能性が高いです。