*Photo credit: Robert Linder

Over the past two years, I picked up stock trading and general finance knowledge as a hobby. There are plenty of things I enjoy here: complex math, understanding trends, and making educated guesses on what happens next. Getting the right tools makes this job a little bit easier.

I use TD Ameritrade for the majority of my trading and learning. They offer a desktop application with a great name: ThinkOrSwim. Using it feels a bit like flying the Space Shuttle at first, but it delivers tons of information and analysis in a small package.

This post isn’t about stock trading – it’s about how to wrestle ThinkOrSwim onto a Fedora Linux machine and get everything working as it should. 🐧

Getting (the right) Java

ThinkOrSwim’s download page mentions installing Zulu OpenJDK 11 first. This is a special certified release of the JDK that is well-tested with ThinkOrSwim.

Visit the RPM-based Linux page for Azul’s OpenJDK and follow the steps they provide:

$ sudo dnf install -y https://cdn.azul.com/zulu/bin/zulu-repo-1.0.0-1.noarch.rpm
$ sudo dnf install zulu11-jdk

If you already have a JDK installed, you’ll need to switch to the Azul JDK as the primary one. I have some other Java applications on my desktop and they seem to work just fine with this JDK. We will use the alternatives script to manage the symlinks for our default JDK:

$ sudo alternatives --config java

There are 2 programs which provide 'java'.

  Selection    Command
-----------------------------------------------
*+ 1           java-17-openjdk.x86_64 (/usr/lib/jvm/java-17-openjdk-17.0.2.0.8-7.fc36.x86_64/bin/java)
   2           /usr/lib/jvm/zulu11/bin/java

Enter to keep the current selection[+], or type selection number:

Press 2 here and then press ENTER. Double check that the Azul JDK is the primary:

$ java --version
openjdk 11.0.14.1 2022-02-08 LTS
OpenJDK Runtime Environment Zulu11.54+25-CA (build 11.0.14.1+1-LTS)
OpenJDK 64-Bit Server VM Zulu11.54+25-CA (build 11.0.14.1+1-LTS, mixed mode)

Adding ALSA support

Yes, we are going back in time and getting ThinkOrSwim talking with ALSA. We will need libasound_module_pcm_pulse.so on the system:

$ sudo dnf whatprovides '*/libasound_module_pcm_pulse.so'
Last metadata expiration check: 3:09:00 ago on Thu 31 Mar 2022 11:14:57 AM CDT.
alsa-plugins-pulseaudio-1.2.6-2.fc36.i686 : Alsa to PulseAudio backend
Repo        : fedora
Matched from:
Filename    : /usr/lib/alsa-lib/libasound_module_pcm_pulse.so

alsa-plugins-pulseaudio-1.2.6-2.fc36.x86_64 : Alsa to PulseAudio backend
Repo        : @System
Matched from:
Filename    : /usr/lib64/alsa-lib/libasound_module_pcm_pulse.so

alsa-plugins-pulseaudio-1.2.6-2.fc36.x86_64 : Alsa to PulseAudio backend
Repo        : fedora
Matched from:
Filename    : /usr/lib64/alsa-lib/libasound_module_pcm_pulse.so

$ sudo dnf install alsa-plugins-pulseaudio

Now that we have ALSA support, let’s move on to configuring the JDK to use it properly. Keith Packard ran into sound problems in Ubuntu and fixed it with a small change to his sound.properties file:

#javax.sound.sampled.Clip=org.classpath.icedtea.pulseaudio.PulseAudioMixerProvider
#javax.sound.sampled.Port=org.classpath.icedtea.pulseaudio.PulseAudioMixerProvider
#javax.sound.sampled.SourceDataLine=org.classpath.icedtea.pulseaudio.PulseAudioMixerProvider
#javax.sound.sampled.TargetDataLine=org.classpath.icedtea.pulseaudio.PulseAudioMixerProvider

javax.sound.sampled.Clip=com.sun.media.sound.DirectAudioDeviceProvider
javax.sound.sampled.Port=com.sun.media.sound.PortMixerProvider
javax.sound.sampled.SourceDataLine=com.sun.media.sound.DirectAudioDeviceProvider
javax.sound.sampled.TargetDataLine=com.sun.media.sound.DirectAudioDeviceProvider

Let’s double check that this will work for the Azul JDK. We need to see if these com.sun.media.sound import paths exists for us:

$ find /usr/lib/j* |grep -i sound
/usr/lib/jvm/java-17-openjdk-17.0.2.0.8-7.fc36.x86_64/lib/libjsound.so
/usr/lib/jvm/zulu11-ca/conf/sound.properties
/usr/lib/jvm/zulu11-ca/lib/libjsound.so

$ strings /usr/lib/jvm/zulu11-ca/lib/libjsound.so | grep DirectAudioDeviceProvider
Java_com_sun_media_sound_DirectAudioDeviceProvider_nNewDirectAudioDeviceInfo
Java_com_sun_media_sound_DirectAudioDeviceProvider_nGetNumDevices
?com/sun/media/sound/DirectAudioDeviceProvider$DirectAudioDeviceInfo
Java_com_sun_media_sound_DirectAudioDeviceProvider_nNewDirectAudioDeviceInfo
Java_com_sun_media_sound_DirectAudioDeviceProvider_nGetNumDevices

Awesome! Those paths match up exactly! 🎉

Let’s find our sound.properties file and make the same modifications:

$ find /usr/lib/jvm -name sound.properties
/usr/lib/jvm/zulu11-ca/conf/sound.properties

Open /usr/lib/jvm/zulu11-ca/conf/sound.properties in your favorite editor and add on Keith’s four lines at the end:

# /usr/lib/jvm/zulu11-ca/conf/sound.properties
javax.sound.sampled.Clip=com.sun.media.sound.DirectAudioDeviceProvider
javax.sound.sampled.Port=com.sun.media.sound.PortMixerProvider
javax.sound.sampled.SourceDataLine=com.sun.media.sound.DirectAudioDeviceProvider
javax.sound.sampled.TargetDataLine=com.sun.media.sound.DirectAudioDeviceProvider

Installing ThinkOrSwim

Head over to ThinkOrSwim’s download page to download the installer. It’s a big installer bundled up inside a shell script (ugly, I know). Run the script with /bin/bash thinkorswim_installer.sh and follow the prompts. I choose to install it only for my user so that it installs in my home directory.

I use i3wm and the installer doesn’t put a desktop file in the right place for me. Here’s what I drop into ~/.local/share/applications/thinkorswim.desktop:

# ~/.local/share/applications/thinkorswim.desktop
[Desktop Entry]
Name=ThinkOrSwim
Comment=ThinkOrSwim Desktop
Exec=/home/major/thinkorswim/thinkorswim
Type=Application
Categories=Finance

Of course, if you aren’t running as the user major or if you installed ThinkOrSwim in a different location, be sure to change the Exec= line above. 😉

Start up ThinkOrSwim using your desktop launcher and enjoy trading on Linux! 🎉