Bonus question: Does anyone know how to play/hear a (financial) time series recorded as a pandas series, dataframe, python list, numpy array, csv/txt file,... ?
This is kind of fun and has practical applications to quantitative finance. My partners and I have actually been experimenting with this as the basis for a model for a short while now and have experienced very interesting results.
At first, I found it most straightforward to map my time series to piano key frequencies. I specifically made a dictionary of piano key frequencies out of just one octave on a piano. The octave consisted of seven white and five black (sharp) keys. Each key was calibrated in relation to the others, just like a piano would be tuned. I "tuned" it to middle C in this way:
$$note frequency = base frequency * 2^\frac N{12} $$
Where the $base frequency$ is that of middle C (261.63 Hz) and each $N$ is a note from C to B (C, c, D, d, E, F, f, G, g, A, a, B). This is known as an equal temperament system.
In Python, we can create a dictionary of frequencies like this:
def piano_notes():
'''
Returns a dictionary containing the frequencies of piano notes
'''
base_freq = 261.63
octave = ['C', 'c', 'D', 'd', 'E', 'F', 'f', 'G', 'g', 'A', 'a', 'B']
note_freqs = {octave[i]: base_freq * 2**(i/12) for i in range(len(octave))}
note_freqs[''] = 0.0 # pause / silent note
return note_freqs
The output from print(note_freqs)
:
{'C': 261.63, 'c': 277.18732937722245, 'D': 293.66974569918125, 'd': 311.1322574981619, 'E': 329.63314428399565, 'F': 349.2341510465061, 'f': 370.00069432367286, 'G': 392.0020805232462, 'g': 415.31173722644, 'A': 440.00745824565865, 'a': 466.1716632541139, 'B': 493.89167285382297, '': 0.0}
From here, you must decide how to transform every price or return in your time series into an integer from 0 - 11 and map them to their respective dictionary values. That step requires some creativity, and I'll leave that to you.
Now that you have your time series mapped to piano note frequencies, to be able to listen to your time series, you need to convert your frequencies into something that can be played, i.e., you need to turn them into sound waves!
A wave can be mathematically described as: $$g(f)=A * \sin(2\pi\ {ft})$$
where: $A$=amplitude, $f$=frequency, and $t$=time. That said, we need to have a function that generates a wave array with respect to time which is much easier than it sounds:
import numpy as np
sample_rate = 44100 # Standard sample rate in digital audio (in Hertz, Hz)
def waves(freq, duration=0.5):
'''
Takes frequency, and time_duration as inputs and returns
a numpy array of values at all points in time
'''
amplitude = 4096 # tuning fork frequency
t = np.linspace(0, duration, int(sample_rate * duration))
wave = amplitude * np.sin(2 * np.pi * freq * t)
return wave
After turning your notes into playable waves, you concatenate them, save them locally, and play them.
import numpy as np
from scipy.io.wavfile import write
def song_data(music_notes):
'''
concatenate all the waves
'''
note_freqs = piano_notes()
song = [waves(note_freqs[note]) for note in music_notes.split('-')]
song = np.concatenate(song)
return song
Here is an example of using the above functions to play "Mary Had A Little Lamb." The file will save in your working directory and can be played using a generic .wav player on just about any machine.
music_notes = 'E-D-C-D-E-E-E--D-D-D--E-E-E--E-D-C-D-E-E-E--E-D-D-E-D-C-'
data = song_data(music_notes)
write('mary-had-a-little-lamb.wav', samplerate, data.astype(np.int16))
Practically speaking, the similarities between the math behind the music and other patterns in nature are extremely interesting. Our original idea has morphed into a full piano (88 keys) with seven octaves and all known chords being played. We have begun to incorporate other instruments recently as well.
I'll leave it to you to determine whether or not the markets are, indeed, playing a song that you like--and can profit from!