Day 7/100 Point and Figure chart
Look at all the Xs and Os!
So, I followed Elon Musk bid for Twitter. I think he's fine, so naturally, I joined Twitter and started this 100 Days of Code. Well, if we're talking about Elon Musk, we're talking about Tesla, and with the recent grape-vine rumors about the price of $TSLA, I'm curious enough to see if now is a good time to buy the stock. Usually I just go by the Bollinger Band price chart, but since I'm in the middle of programming project, why not? Let's just do it myself!
I'll probably do a bar/line chart later, but right now, the one I want to concentrate is the Point and Figure chart, also known as the XO chart. If the price rises, you put in X. If the price lowers, you put in O. Time is irrelevant.
Well, I have trouble with that. I think the value of time is significant, so I decided to just do one with time stamp. And it's not too difficult. It's simply an increment of counter, and the way I set it up, it's just a simple integer. If you want to show date, though, you only need to change one line. Here's the diff:
73c73
< printf("%d\t",tstamp);
---
> printf("%s\t",Date);
And running the program will yield this output:
2022-01-04 XXXXXXXX
2022-01-10 OOOOOOOOO
2022-01-13 XXXXX
2022-01-14 OOOOO
2022-01-18 XX
2022-01-26 OOOOOOO
2022-01-27 XX
2022-01-28 OOOOOO
2022-02-02 XXXXXX
2022-02-04 OOO
2022-02-10 XXX
2022-02-14 OOOOO
2022-02-17 XXXX
2022-02-24 OOOOOOOO
2022-03-03 XXXXXX
2022-03-08 OOOO
2022-03-10 XXX
2022-03-15 OOOOO
2022-03-31 XXXXXXXXXXXXXXXX
2022-04-01 OO
2022-04-05 XXXX
2022-04-13 OOOOOOOO
2022-04-14 XXX
2022-04-18 OOO
2022-04-20 XXX
2022-04-21 OOO
2022-04-26 XX
2022-05-02 OOOOOOO
2022-05-05 XXXXX
2022-05-10 OOOOOOOOO
2022-05-11 XX
2022-05-13 OOOOO
2022-05-16 XXX
2022-05-17 OOO
2022-05-18 XXX
2022-05-23 OOOOOO
2022-05-24 XX
2022-05-25 OOO
2022-06-01 XXXXXXX
2022-06-02 OO
2022-06-03 XXX
2022-06-06 OOOOO
2022-06-10 XX
2022-06-15 OOOO
2022-06-16 XXX
2022-06-17 OOOO
So, there you go. But what is the core of the program? What makes Point and Figure program what it is? Well, there's the XO chart:
void mark(int n) {
char c;
for (n=lslot;n>=slot;n--) {
if (dir<0) c='O';
if (dir==0) c='=';
if (dir>0) c='X';
if (n<=0) { c='|'; n=0; }
if (n>=sca-1) { c='+'; n=sca-1; }
scan[n]=c;
}
for (n=lslot;n<=slot;n++) {
if (dir<0) c='O';
if (dir==0) c='=';
if (dir>0) c='X';
if (n<=0) { c='|'; n=0; }
if (n>=sca-1) { c='+'; n=sca-1; }
scan[n]=c;
}
}
Doing it that way annoys me to no end. Isn't there a simpler way to do it? Yes, I'm sure of it. But this is just something I do at the end of the day for one hour at a time. There's not a whole lot that I can experiment with. I did experiment a bit, but the charting process got messed up. Even now, there's a little extra character on the chart, which I'm sure you notice if you look at traditional charts. However, it's sufficient for now.
There are three other characters added, as compared to the traditional chart. They are '=' to denote non changing price. '|' to denote lower bound of the chart. '+' to denote upper bound of the chart. This is because the chart is non-self adjusting. It has the benefit of zooming in to the section you're interested in, as opposed to having to take everything in.
Of course, chart legends is only the presentation aspect of it. How about the logic? The following logic is more complicated than it needs to be. That's because I'm still figuring out as I go. I'm not really satisfied with it, but like I said, I ran out of time. And I will probably use the knowledge of it for the future.
In particular, I would move the sscanf() line to its own function. There's also a whole lot of calculation about ranges, scale, and direction. Those can be simplified further.
int pnf() {
char line[MAXSTR];
while (gets(line)!=NULL) {
sscanf(line,"%s\t%f\t%f\t%f\t%f\t%f\t%f\n"
,Date,&Open,&High,&Low,&Close,&Adj,&Volume);
if (tmin>Adj) tmin=Adj; if (tmax<Adj) tmax=Adj;
slot=(int)(((Adj-min)*(float)sca/(max-min)))+1;
slot=(slot<1)?0:(slot>sca-2)?sca-1:slot;
if (val<0) { val=Adj; lslot=slot; clrscan(); }
if (dir>0 && slot<lslot) { showScan(1); dir=-1; }
if (dir<0 && slot>lslot) { showScan(1); dir=1; }
if (dir==0 && slot<lslot) { dir=-1; }
if (dir==0 && slot>lslot) { dir=1; }
mark(slot); val=Adj; lslot=slot; tstamp++;
} return 0;
}
Looking at the forest point of view, it looks like I deal with a lot of different states of variables. So, I'd do better by setting up flags, and perhaps, setting them up in an array. That way I can just do a simple lookup function and clean up the code that way.
The way it is now, it works. Not pretty, but it works. All in all, I'm satisfied with what I have.
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#define MAXSTR 256
int Debug=0;
float min=5.0;
float max=20000.0;
int sca=20;
char scan[MAXSTR];
int tstamp=0;
int dir=0;
float val=-1;
int slot,lslot;
char Date[MAXSTR];
float Open;
float High;
float Low;
float Close;
float Adj;
float Volume;
float tmin=20000.0;
float tmax=0.0;
void ShowMsg() {
puts("
Usage: pnf [OPTION]...
Display Point and Figure chart given a stock.csv file.
Min and Max values are not automatically adjusted!
Default values are:
-nN minimum value. Default N0
-xN maximum value. Default N200
-sN scales. Default N20
--help display this help and exit
--debug debug info
");
}
int GetOpt(int argc, char *argv[]) {
int i;
for (i=1;i<argc;i++) {
if (!strncmp("-n",argv[i],2)) min=atof(argv[i]+2);
if (!strncmp("-x",argv[i],2)) max=atof(argv[i]+2);
if (!strncmp("-s",argv[i],2)) sca=atoi(argv[i]+2);
if (!strncmp("--help",argv[i],6)) {
ShowMsg(); return 0;
}
if (!strncmp("--debug",argv[i],7)) Debug=1;
}
if (Debug) {
puts("Debug...");
printf("Min: %0.2f\n",min);
printf("Max: %0.2f\n",max);
printf("Scale: %d\n",sca);
}
return 0;
}
void clrscan(void) {
int i;
for (i=MAXSTR;i>0;) scan[--i]=' ';
}
void showScan(int mode) {
int i;
printf("%d\t",tstamp);
for (i=0;i<sca;i++) putchar(scan[i]);
putchar('\n');
if (mode) { clrscan(); }
}
void mark(int n) {
char c;
for (n=lslot;n>=slot;n--) {
if (dir<0) c='O';
if (dir==0) c='=';
if (dir>0) c='X';
if (n<=0) { c='|'; n=0; }
if (n>=sca-1) { c='+'; n=sca-1; }
scan[n]=c;
}
for (n=lslot;n<=slot;n++) {
if (dir<0) c='O';
if (dir==0) c='=';
if (dir>0) c='X';
if (n<=0) { c='|'; n=0; }
if (n>=sca-1) { c='+'; n=sca-1; }
scan[n]=c;
}
}
int pnf() {
char line[MAXSTR];
while (gets(line)!=NULL) {
sscanf(line,"%s\t%f\t%f\t%f\t%f\t%f\t%f\n"
,Date,&Open,&High,&Low,&Close,&Adj,&Volume);
tmin=(tmin<Adj)?tmin:Adj;
tmax=(tmax>Adj)?tmax:Adj;
slot=(int)(((Adj-min)*(float)sca/(max-min)))+1;
slot=(slot<1)?0:(slot>sca-2)?sca-1:slot;
if (val<0) { val=Adj; lslot=slot; clrscan(); }
if (Debug) printf("%s\t%0.2f (%d) \tMin=%.2f\tMax=%.2f\n",Date,Adj,slot,tmin,tmax);
if (Debug) {
printf("(%d)\t",slot);
}
if (dir>0 && slot<lslot) { showScan(1); dir=-1; }
if (dir<0 && slot>lslot) { showScan(1); dir=1; }
if (dir==0 && slot<lslot) { dir=-1; }
if (dir==0 && slot>lslot) { dir=1; }
mark(slot);
val=Adj; lslot=slot; tstamp++;
}
return 0;
}
int main (int argc, char *argv[] ) {
int e=0;
char line[MAXSTR];
GetOpt(argc, argv);
if (gets(line)!=NULL) e=pnf(); //Remove header
return e;
}
One more thing: (spoiler!)
Yes, now is a good time to buy Tesla stock! I'd wait for an uptick, though.
No comments:
Post a Comment