Vykresľovanie sviečkového grafu

Zápisník experimentátora

Hierarchy: Sviečkové grafy

V pokračovaní sviečkových grafov sa vrhneme od teórie ku konkrétnemu zobrazeniu. Nebudeme ešte zobrazovať reálne hodnoty, ale zobrazíme si niekoľko vzorových grafov. Na obrázku sú dva typy sviečkových grafov. Jeden je vykresľovaný pomocou čiar a druhý pomocou stĺpcov. Nadviažeme tak na predchádzajúci článok, kde sme prebrali teóriu sviečkových grafov.

Graf je zobrazený na displeji Nokia 5110 a po bokoch displeja sú zobrazené pomocné mierky. Je vykreslených 7 stĺpcov. Pretože by sa vedľa seba toľko stĺpcov nezmestilo, hodnoty pre grafy sú navrhnuté tak, aby boli prvé dva stĺpce s malými hodnotami a ostatné stĺpce sa v prípade presahu vykreslili nad nimi.

Čiarová forma sviečkových grafov je vykreslená pomocou troch úsečiek. Prvá vodorovná úsečka zobrazuje hodnotu open. Druhá zvislá úsečka zobrazuje hodnoty high-low. A posledná úsečka zobrazuje hodnotu close.

Stĺpcová forma sviečkových grafov je vykreslená pomocou úsečiek a prázdnych alebo vyplnených stĺpcov. Pretože Nokia 5110 je monochromatický displej, použijeme na kreslenie rastúceho grafu prázdny stĺpec a na kreslenie klesajúceho vyplnený stĺpec. Tak je to zaužívané aj vo svete finančných grafov.

Program

Na vykreslenie grafov poslúži tento program. Na dôležitých miestach je vysvetlené jeho správanie.

#include <Adafruit_GFX.h>
#include <Adafruit_PCD8544.h>

// Software SPI (slower updates, more flexible pin options):
// pin 7 - Serial clock out (SCLK)
// pin 6 - Serial data out (DIN)
// pin 5 - Data/Command select (D/C)
// pin 4 - LCD chip select (CS)
// pin 3 - LCD reset (RST)
Adafruit_PCD8544 display = Adafruit_PCD8544(7, 6, 5, 4, 3);

struct OHLCData {
  int o;
  int h;
  int l;
  int c;  
};

OHLCData ohlc[]={
  {1,3,0,2},
  {1,3,0,1},
  {20,40,10,30},
  {30,35,15,20},
  {20,27,16,25},
  {25,43,23,35},
  {35,37,30,31},
};

const int ohlccount=sizeof(ohlc)/sizeof(OHLCData);

V štruktúre OHLCData ukladám hodnoty open, high, low a close. V poli ohlc je uložených niekoľko vzorových hodnôt. Vyberal som také, ktoré sa na grafoch pekne ukážu.

void setup() {
  display.begin();
  display.setContrast(60);
  display.clearDisplay();

  for(int j=0;j<=84;j+=2)
    display.drawLine(j,0,j,(j%10==0 ? 2 : 1),BLACK);
  for(int j=0;j<=38;j+=2)
    display.drawLine(0,47-j,(j%10==0 ? 2 : 1),47-j,BLACK);

  for(int i=0;i<ohlccount;i++) {
    drawBarLine(i, &ohlc[i]);
    drawBarBar(i, &ohlc[i]);
  }

  display.display();
}

Tu inicializujem displej Nokia 5110, kreslím mierku a vykresľujem aj samotné grafy. Pri kreslení mierky som si pomohol kódom z článku o analógovom voltmetri. Tu je použitý ten istý algoritmus, akurát je kreslený na obe osi. Treba si len dať pozor na vykresľovanie zvislej osi, kde musíme otočiť výpočet a od čísla 47 (najvyššia hodnota riadku) odpočítať premennú j, aby sme dosiahli vykresľovanie od spodného okraja displeja.

Samotné stĺpce kreslia dve funkcie. Jedna kreslí čiarovú formu a druhá stĺpcovú. Ako parameter do nich ide &ohlc[i]. To má nasledovný význam. Najprv sa získa hodnota z poľa ohlc na pozícii i a tá sa dereferencuje na pointer, ktorý sa predá ako parameter do funkcie. Čiže namiesto celej hodnoty open, high, low a close, ktorá by sa musela skopírovať do zásobníka, sa do zásobníka skopíruje iba pointer. Výsledkom je kratší kód, čo je na mikrokontroléroch s malou pamäťou dosť dôležité.

void loop() {
}

Vo funkcii loop nemusíme robiť nič, pretože sa jedná o vykreslenie statického obrázku, ktorý sme už vykreslili vo funkcii setup.

void drawBarLine(int pos, OHLCData *bar) {
  int START = 5 + pos * 6;
  const int BW = 2;
  const int HEIGHT = 47;

  // open
  display.drawLine(START,HEIGHT-bar->o,START+1*BW,HEIGHT-bar->o,BLACK);

  // high - low
  display.drawLine(START+1*BW,HEIGHT-bar->h,START+1*BW,HEIGHT-bar->l,BLACK);

  // close
  display.drawLine(START+1*BW,HEIGHT-bar->c,START+2*BW,HEIGHT-bar->c,BLACK);
}

Popis tehto funkcie skoro nepotrebuje komentár. Iba musíme nakresliť tri úsečky. Premenná START určuje pozíciu stĺpca od ľavého okraja displeja. Premenná BW určuje šírku stĺpca. Premenná HEIGHT nám pomáha otočiť výsledok vykreslenia v osi Y, pretože displej vykresľuje riadok s indexom 0 hore a riadok s indexom 47 dole a my to potrebujeme naopak.

void drawBarBar(int pos, OHLCData *bar) {
  int START = 40 + pos * 6;
  const int BW = 2;
  const int HEIGHT = 47;
  int top;
  int bottom;

  if(bar->o < bar->c) {
    top = bar->c;
    bottom = bar->o;
  } else {
    top = bar->o;
    bottom = bar->c;
  }

  // high
  display.drawLine(START+1*BW,HEIGHT-bar->h,START+1*BW,HEIGHT-top,BLACK);

  // low
  display.drawLine(START+1*BW,HEIGHT-bar->l,START+1*BW,HEIGHT-bottom,BLACK);

  // bar
  if(bar->o < bar->c) 
    display.drawRect(START,HEIGHT-bottom,2*BW+1,bottom-top+1,BLACK);
  else
    display.fillRect(START,HEIGHT-bottom,2*BW+1,bottom-top+1,BLACK);
}

Táto funkcia je zložitejšia, pretože najprv potrebujeme určiť, či sa jedná o stúpajúci graf, alebo sa jedná o klesajúci graf. A podľa toho dokresľujeme telo samotného stĺpca. Telo sa kreslí rozdielne pre stúpajúci a pre klesajúci graf.

Zdrojový kód

Zdrojový kód programu sa nachádza na GitHub.

Pokračovanie

V pokračovaní sa pozrieme na vizualizáciu náhodných údajov pomocou algoritmov, ktoré sme naprogramovali v tomto článku.


31.07.2016


Menu