1: #!/usr/bin/perl
2:
3: =head1 NAME
4:
5: ticker - A stock monitoring client written for Peep: The Network
6: Auralizer.
7:
8: Peep takes virtual events and turns them into unobtrusive sounds like
9: birds chirping, rain falling, crickets doing whatever crickets do,
10: etc. It is frequently used for network and systems monitoring because it
11: is well suited for that task, but I recently added an API
12: to Net::Peep that allows customized clients to be built with very
13: little code.
14:
15: Since there are some stocks I follow I decided to make my first custom
16: client a stock ticker. Thanks to Finance::Quote, it took me about a
17: half-hour to write :-) A rooster crow or doorbell chime tells me that
18: something is happening with my stocks. And at the end of the day, it
19: e-mails me a report on any questionable activity.
20:
21: Though it is heavily commented, the code itself is quite short.
22:
23: =head1 USAGE
24:
25: ./ticker --help
26:
27: ./ticker --noautodiscovery --server=localhost --port=2001 --nodaemon
28:
29: If you have any problems, try turning on debugging output with
30: something like --debug=9.
31:
32: =head1 CONFIGURATION
33:
34: To use this client, include a section like the following in peep.conf:
35:
36: client ticker
37: class home
38: port 1999
39: config
40: # Stock symbol Stock exchange Event Max Min
41: RHAT nasdaq red-hat 4.0 3.6
42: SUNW nasdaq sun-microsystems 9.0 8.0
43: end config
44: notification
45: notification-hosts localhost
46: notification-recipients bogus.user@bogusdomain.com
47: notification-level warn
48: end notification
49: end client ticker
50:
51: and another section in the events block with something like
52:
53: events
54: #Event Type | Path to Sound File | # of sounds to load
55: ...
56: red-hat /usr/local/share/peep/sounds/misc/events/rooster.* 1
57: sun-microsystems /usr/local/share/peep/sounds/misc/events/doorbell.* 1
58: end events
59:
60: =head1 AUTHOR
61:
62: Collin Starkweather <collin.starkweather@colorado.edu> Copyright (C) 2001
63:
64: =head1 SEE ALSO
65:
66: perl(1), peepd(1), Net::Peep, Net::Peep::Client, Net::Peep::BC,
67: Net::Peep::Notifier, Net::Peep::Notification, Finance::Quote
68:
69: http://peep.sourceforge.net
70:
71: =cut
72:
73: # Always use strict :-)
74: use strict;
75: use Net::Peep::BC;
76: use Net::Peep::Log;
77: use Net::Peep::Client;
78: use Net::Peep::Notifier;
79: use Net::Peep::Notification;
80: use Finance::Quote;
81: use vars qw{ %config $logger $client $quoter $conf };
82:
83: # The Net::Peep::Log object will allow us to print out some things in
84: # a nicely formatted way so we can tell ourselves what we're doing ...
85: $logger = new Net::Peep::Log;
86:
87: # Instantiate a Peep client object. The client object handles most of
88: # the dirty work for us so we don't have to worry about things such as
89: # forking in daemon mode or parsing the command-line options or the
90: # Peep configuration file.
91: $client = new Net::Peep::Client;
92: $quoter = new Finance::Quote;
93:
94: # First we have to give the client a name
95: $client->name('ticker');
96:
97: # Now we initialize the client. If the initialization returns a false
98: # value, we display documentation for the script and exit.
99: $client->initialize() || $client->pods();
100:
101: # Now we assign a parser that will parse the 'ticker' section of the
102: # Peep configuration file
103: $client->parser( \&parse );
104:
105: # Now we tell the client to get the information from the configuration
106: # file. It returns a Net::Peep::Conf, the Peep configuration object,
107: # which contains information from the configuration file.
108: $conf = $client->configure();
109:
110: # Register a callback which will be executed every 60 seconds. The
111: # callback is simply a function which checks the price of the stock
112: # and peeps every time it exceeds the maximum or minimum price that
113: # has been set.
114: $client->callback( \&loop );
115:
116: # Start looping. The callback will be executed every 60 seconds ...
117: $client->MainLoop(60);
118:
119: sub parse {
120:
121: # Parse the config section for the ticker client in the Peep
122: # configuration file
123:
124: # We'll use a regular expression to extract the ticker information
125: # and stuff it into a data structure (the global variable %config)
126:
127: # This subroutine will be used to parse lines from peep.conf such
128: # as the following and store the information in %config:
129: # RHAT nasdaq red-hat 4.0 3.6
130: # SUNW nasdaq sun-microsystems 9.0 8.0
131: for my $line (@_) {
132: if ($line =~ /^\s*([A-Z]+)\s+(\w+)\s+([\w\-]+)\s+([\d\.]+)\s+([\d\.]+)/) {
133: my ($symbol,$exchange,$event,$max,$min) = ($1,$2,$3,$4,$5,$6);
134: $config{$symbol} = { event=>$event, exchange=>$exchange, max=>$max, min=>$min };
135: }
136: }
137:
138: } # end sub parse
139:
140: sub loop {
141:
142: for my $key (sort keys %config) {
143: $logger->log("Checking the price of [$key] ...");
144: # Fetch some information about the stock including the price
145: my %results = $quoter->fetch($config{$key}->{'exchange'},$key);
146: my $price = $results{$key,'price'};
147: $logger->log("\tThe price of [$key] is [$price].");
148: if ($price > $config{$key}->{'max'} or $price < $config{$key}->{'min'}) {
149: $logger->log("\tThe price is out of bounds! Sending notification ....");
150: # The price is out of bounds! We'll start peeping ...
151: my $broadcast = Net::Peep::BC->new('ticker',$conf);
152: $broadcast->send('ticker',
153: type=>0,
154: sound=>$config{$key}->{'event'},
155: location=>128,
156: priority=>0,
157: volume=>255);
158: # In case we're away from our desk, we'll also send out an
159: # e-mail notification. Don't want to miss the action!
160: my $notifier = new Net::Peep::Notifier;
161: my $notification = new Net::Peep::Notification;
162: $notification->client('ticker');
163: $notification->status('crit');
164: $notification->datetime(time());
165: $notification->message("The price of $key is $price!");
166: $notifier->notify($notification);
167: }
168: }
169:
170: } # end sub loop
171:
172: __END__