Saturday, December 26, 2020

Run Code on Raspberry Pi

  • Why? Cheap, consumes little power, generates little heat/noise. Protected from external attacks because accessible only thru local wifi (by default).
  • Required:
    • Raspberry Pi (e.g. model 3B+)
    • 16gb microSD card (min 8gb)
    • Case (helps protect against short circuiting when e.g. water spills)
    • SD card adapter (microSD->SD) - to move files btwn laptop (SD size) & raspberrypi (microSD size)
    • AC Adapter (output >2.5amps)(power to run wifi 24/7)
    • Cable (able to carry >2.5amps)
    • An old HDMI monitor and USB wired keyboard
  • Steps
    • Insert microSD (via SD card adapter) into laptop. Ensure the switch on the SD card adapter is in the "unlock for read/write" UP position.
    • Get OS ready: 
      • https://dietpi.com (lightweight version of Raspbian OS) > download image for "Raspberry Pi" into laptop
      • Unpack file with 7-zip
    • Download software to flash (create bootable disk) the microSD:
      • Download https://www.balena.io/etcher/ & run > click "Select Image" > navigate to & select dietpi disc image (Example: DietPi_v6.25_RPi-ARMv6-Buster.iso , size: ~944mb) > click "Mass Storage USB Device" > select the microSD drive > click "Flash " (will format microSD & write OS) > once finish installing it will auto-unmount (note: DO NOT proceed with the suggestion to format the drive)
      • Reinsert microSD > observe 2 drives created (1 boot drive ~250mb + 1 inaccessible drive to store programs later) (if unable to detect the "inaccessible drive", it's still ok - confirmed? yes)
      • Note: Possible to configure boot settings (but we don't do it this round). If interested: Edit boot drive > 
        • dietpi.txt >
          • IP address for raspberrypi
        • dietpi-wifi.txt>
    • Insert microSD into RaspberryPi slot
    • Setup wifi:
      • Connect raspberrypi (via HDMI) to any old monitor (& wired keyboard & within wifi coverage) then only connect to cable & AC Adapter & switch on power. (self note: what worked: 2m long hdmi tv cable + old samsung monitor + wifi router is VERY VERY NEAR to the RP). 
      • On 1st time boot (aim: connect to wifi)
        • if see the status "Under-voltage detected!", then try to use >2amps output ac adapter (e.g. 2.5amps or 3.0amps)
        • DietPi Login:
          • Username: root
          • Password: dietpi
        • "unable to resolve host... raspbian.raspberrypi.org" (because wifi not yet setup)
        • "DietPi-Config: Edit network,..."
        • "7 : Network Options: Adapters"
        • "WiFi : Not Found | [Off] | Disconnected" > Enable wifi (OK)
        • "WiFi : Available | [On] | Disconnected"
          • "Scan : Scan and configure SSID" > select 1st slot > "Scan : Scan and configure an SSID" > select wifi name (note: 5g didnt seem to work. Trying 3g now. Works? YES) > "WPA-PSK" > enter wifi pwd > DONE > "Apply : Save all changes and restart networking" > Save all changes and restart networking (OK) > ...restarting... > Back
        • "WiFi : Available | [On] | Connected" > Note the next screen shows router's address details
          • click "Auto Reconnect : [Off]" to change it to "Auto Reconnect: [On]"
          • "Change Mode : [DHCP]" > pls ensure only 1 SSID exist in the scan menu (ok) > changes to "Change Mode : [STATIC]"
          • "Copy : Copy current address to "Static""
          • "Static IP : [192.168.0.155] ==> Your RP's IP address. Write it down.
          • "Static DNS : [xxx.xxx.x.x]" > select Google's custom DNS server
          • "Apply : Save all changes and restart networking" > Save all changes and restart networking (OK) > ip static address change detected, pls reboot (OK) > Back
        • [somewhere along the way, before rebooting, remember to turn on "onboard wifi"]
        • "Test : Run internet connection test" > "https://www.google.com" > will see "Test : [Success] | Online" > Back > (goes to menu with 10 options) > Exit > reboot is required (ok)
        • select "...Edit network... settings" > "7. Network options: adapters" > Wifi (enable now) > RP will scan the available wifis > select "Wifi: Available|[On]|..." > select "Scan and configure SSID" > select 1st slot > Scan > select your wifi network > WPA-PSK > enter wifi pwd > select newly set-up wifi and click "DONE" > select "Apply: save all changes and restart networking" > Ok > Note the new window (these new details are provided by the router)
        • Once connected to wifi, set a static IP for RP (thereafter RP no need monitor/keyboard anymore, RP can be controlled thru wifi)
          • Select "Change Mode: [DHCP]" > OK (this changes "DHCP" to "Static IP")
          • select "Copy current address to Static"
            • static IP
            • static mask
            • static gateway (this is the router's IP address)
            • static DNS server (this server tells u the IP address for any website name) > change to dns server by Google
          • select "Apply: save all changes and restart networking" > Ok > (ignore the "reboot is required" message)
          • change "Auto-reconnect (on)" > OK
          • Go Back
        • select "Test: Run internet connection test" > www.google.com > observe: "Test: [Success] | Online" > Exit DietPi-Config (back x 3 then exit) > RP will reboot
        • Note: If see msg "Waiting for valid network connection timed out" appears, may have to redo the above & reboot several times until connection is successful. Warning: Always choose "CANCEL" when asked "Would you like to purge all WiFi related APT packages?" (when u try to off the wifi, in order to redo the above steps)
      • On 2nd time boot
        • [At this point, RP is connected to wifi and always has a fixed IP address. Can disconnect monitor & keyboard.]
        • Observe the messages, including: RP will extend usable space on microSD... will take time (time taken ~1hour 40minutes) as RP updates & upgrades itself.
        • Change default global software password? > Yes > enter new pwd
        • Change existing unix user pwd? > Yes > enter new pwd
        • Disable serial console? > OK (to make RP faster) (serial console was used to run older hardware)
        • "DietPi-Software" menu > select "SSH Server" > change "Dropbear" to "OpenSSH" (enables u to give it commands via terminal and transfer files) > OK > Install (start installation of OpenSSH) > (at this point, u can log into raspberry (as long as know its IP and connected to wifi & has electricity) > OK (to reboot after finished installing OpenSSH) (time taken ~4minutes)
      • On 3rd boot
        • Login:
          • DietPi login (username): root
          • Password: (new pwd which was recently changed)
        • See prompt "user1@computer1~$". Can do further updates here ("sudo dietpi-update" > Apply update>OK), but not necessary.
        • Test if RP is connected to internet: "ping 8.8.8.8" (pings a google address) > receive replies of 200-300 milliseconds (means connected to internet)
        • Do a proper shutdown of the RaspberryPi: Wait until see the prompt, then enter "sudo shutdown now". Wait for yellow blinking read/write led light to stop completely. (Shutting down by simply turning off the power supply to the RP (without waiting for led lights to stop blinking completely) can result in corrupted microSD card).
        • [To be safe, test rebooting once before going to the next step (unpluggin the monitor n keyboard). Bcos RP could have kernal-related booting problems. So, (1) "sudo reboot now", watch it (hopefully) boot successfully and reach the command prompt, then "sudo shutdown now", then turn switch off/on and (hopefully) boot succesfully. Then finally, "sudo shutdown now" and proceed to next step.]
        • Unplug the monitor (HDMI) and keyboard (USB), let RP continue connection to wifi and power supply.
      • Connect to RP via wifi
        • Turn on RP (only shd be connected to wifi and cable for power supply)
        • Turn on laptop, go to command prompt:
          • Ping the static IP address of the RP: "ping 192.168.0.155" (get this RP IP address from (1) static IP in the DietPi-Config menu; or (2) when newly booted, the line just prior to "please hit return to login" e.g. LAN IP: 192.168.0.155 (wlan0))
          • If response is "sent x packets, received x packets, time taken z ms" (instead of "Destination host unreachable"), then RP has booted, connected to wifi, and is online.
    • Use laptop to communicate with RP using SSH (secured connection using a pwd)
      • Putty
        • https://www.putty.org/ (MSI windows installer, 64-bit) > install & run >
          • Session > Hostname > IP address: 192.168.0.155
          • Port: 22 (for SSH)
          • Connection type: SSH
        • > Open > Putty security alert (Yes)  (to get imprint of RP for security purpose) (on hindsight: hit "NO" to avoid future connection issues?)
        • Putty window: (connect as normal user, no need root user as all software has been installed)
          • login:  
            • username: dietpi
            • pwd: insert pwd which was recently changed
          • arrive at prompt: dietpi@DietPi:~$
          • Increase font size: click topleft corner of window > Change Settings > Category: Window: Appearance > Font settings: Change : Size (from 10 to 14) 
          • dietpi@DietPi:~$ ls (no files are listed at this moment)
          • RP's "task manager": dietpi@DietPi:~$ htop  (from top to bottom:)
            • see 4 cores
            • Mem (current usage / total RAM)
            • Swp (virtual RAM from SD card's space)
            • Processes
              • keyup/keydown to select a process (e.g. SSH, wpa_supplicant (which handles wifi))
              • F3 to search for a process
              • F9 to kill a selected process > pick "SIG(nal) KILL"
          • dietpi@DietPi:~$ exit (to close Putty)
      • Secure Shell App (Chrome extension; alternative to Putty)
        • google "chrome SSH" > download "Secure Shell App" extension for chrome
        • Enter:
          • username: dietpi
          • name of host machine: 192.168.0.155
          • port: 22 (for SSH)
        • hit "Enter(to connect)" (or click on "dietpi@192.168.0.155:22" (highlighted red) then click top right button)
          • Errors?
            • "WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!.... It is also possible that a host key has just been changed.... Offending ECDSA key in /.ssh/known_hosts:... ECDSA host key for ... has changed and you have requested strict checking.... Host key verification failed." ==> Delete entry in chrome browser: Chrome > 3dots > More tools > Extensions > Chrome Apps (Secure Shell App) > Details > Extension Options > "SSH Files" category > DELETE THE OFFENDING ROW > Save (source https://stackoverflow.com/questions/63815375/warning-remote-host-identification-has-changed-chrome-secure-shell-app-extensi)
            • "kex_exchange_identification: Connection closed by remote host Connection closed by UNKNOWN port -1" OR " ==> Off then on RP
        • Type "yes" to get fingerprint of computer. If a connection error occurs, just hit "R" to try reconnect again.
        • enter pwd for "dietpi" user (created earlier)
        • Arrive at command prompt "dietpi@DietPi:~$"
    • Install Freqtrade (UPDATE: skip this entire section (bullet point). Instead, install "pip", "git", "virtualenv", "freqtrade" as per https://hacks-for-life.blogspot.com/2020/12/freqtrade-notes.html . Works? No. Error. Proceed with this section first, as usual..)
      • Connect laptop to freqtrade via Putty:
        • Hostname: 192.168.1.26 | Port: 22 | Connection type: SSH > Open  > login as: dietpi:[pwd] > arrive at prompt "dietpi@DietPi:~$ "
      • Goto https://gist.github.com/ilyasst/e953c0f9a916eecdb6a81f96e6d042ba > click "Raw" > copy entire URL (ends with ".sh")
      • Goto Putty window. Type "wget" (dload the url's contents), then rightclick to paste.
        • dietpi@DietPi:~$ wget https://gist.githubusercontent.com/ilyasst/e953c0f9a916eecdb6a81f96e6d042ba/raw/ed8276ec1790e91e70041f3418006115a8c709cc/install_freqtrade_rpi.sh
        • dietpi@DietPi:~$ ls (will display the downloaded file i.e. install_freqtrade_rpi.sh)
        • dietpi@DietPi:~$ cat install_freqtrade_rpi.sh (to see contents of file)
        • dietpi@DietPi:~$ nano temp.sh (create a temporary text file that contains commands)
        • copy the contents from URL and paste (rightclick) into nano window
        • Save file: Ctrl+X > Yes > Enter
        • dietpi@DietPi:~$ ls (display 2 files: install_freqtrade_rpi.sh & temp.sh)
        • dietpi@DietPi:~$ cat temp.sh (see contents of file -shd be same)
        • dietpi@DietPi:~$ rm temp.sh (remove temp.sh bcos same as install_freq....)
        • dietpi@DietPi:~$ ls (see only 1 file: install_freqtrade_rpi.sh)
        • dietpi@DietPi:~$ sh ./install_freqtrade_rpi.sh (to install freqtrade on RP)
          • installation takes many minutes.. installs python3.7 for RP, etc. (takes ~32minutes)
          • Do you want to continue? Yes (then takes another ~19minutes)
          • Reset git branch? YES (yes for new installation (vs modification) of freqtrade). Will install latest version of freqtrade in RP.
          • Install dependencies for dev? NO (not developing software on RP, keep RP free of bloat)
          • Install plotting dependencies? No [selfnote: kiv to change this in future, if necessary]
          • Generating general configurations: hit Enter for all. (then takes another ~53 minutes before hitting the following error...) [selfnote: midway thru installation, error occurred: "Problem with the CMake installation, aborting build. CMake executable is cmake". Retried with installation steps at https://hacks-for-life.blogspot.com/2020/12/freqtrade-notes.html (installed: pip, git, virtualenv, latest version of freqtrade). Works?yes. (if didnt work, to try latest installation steps at https://www.freqtrade.io/en/latest/installation/ )(including specific steps for RP)]
          • Note: If laptop and RP disconnects during installation, just restart (begin new session/window with Putty).
        • Highlight (with mouse cursor) the text "", it will be copied, then rightclick to paste.
        • dietpi@DietPi:~$ cd freqtrade
        • dietpi@DietPi:~/freqtrade$ source .env/bin/activate
        • (.env) dietpi@DietPi:~/freqtrade$ freqtrade (to check freqtrade works, but without access to login details e.g. telegram)
        • Goal: RP runs freqtrade for months; if freqtrade crashes RP reboots and autoruns freqtrade; and use SSH to easily transfer new config & strategy files from laptop to RP.
    • Transfer data from laptop to RP
      • Connect RP to power supply and SSH (red & yellow LEDs turn on)
      • Run the Chrome extension SSH app
        • google "chrome SSH" > download "Secure Shell App" extension for chrome
        • Enter:
          • username: dietpi
          • name of host machine: 192.168.0.155
          • port: 22 (for SSH)
        • click Connect (first click on "dietpi@192.168.0.155:22", then click top right button)
        • type "yes" to get fingerprint of computer
        • enter pwd for "dietpi" user (created earlier)
        • Arrive at command prompt "dietpi@DietPi:~$" (u r now in RP)
      • dietpi@DietPi:~$ cd freqtrade
      • dietpi@DietPi:~/freqtrade$ source .env/bin/activate
      • Ideally, create config & strategy files in laptop using atom, then transfer to RP. Transfer using Filezilla FTP Client: (https://filezilla-project.org/)
        • Host: 192.168.0.155
        • username: dietpi
        • pwd: (as earlier)
        • Port: 22
        • click "Quick Connect" (note: if later status shows "Disconnected from server", just reconnect with option "abort previous connection and connect in current tab")
          • Errors?
            • "FATAL ERROR: Network error: Software caused connection abort Error: Could not connect to server" ==> Off then on RP
      • Config file
        • RHS panel: /home/dietpi/freqtrade/config.binance.json.example; drag to LHS panel
        • Edit config in atom (fill exchange's key & secret, telegram's token & chat_ID, rename to "config.json")
        • config.json file @ LHS panel --> drag to RHS panel: /home/dietpi/freqtrade/ and overwrite if necessary.
      • dietpi@DietPi:~/freqtrade$ freqtrade (start freqtrade, load config, use default strategy, will see error "Telegram Network Error: Chat not found...")
        • To fix this telegram error, u must contact your bot directly first (this avoids other bots spamming u with unwanted msgs). So, go to BotFather chat, click on the link to your bot e.g. "t.me/freqtradeybot", and send any msg. Then recheck the SSH messages. Should now see "Bot heartbeat.. etc".
      • At this point, RP is running freqtrade, connected to power supply.
    • Run freqtrade using SCREEN (i.e. runs 24/7, without connected to laptop, without an opened terminal, and without auto-restart if freqtrade crashes)
      • Open Chrome SSH and connect to RP
        • Note: Use Chrome SSH bcos it's available across all OS. Putty is not.
      • Install Screen (this enables multiple terminal windows which can run in background)
        • dietpi@DietPi:~$  sudo apt install screen (request admin rights (sudo) to goto app store (apt) and install the app called "screen")
        • dietpi@DietPi:~$  screen (this begins a new virtual terminal) > hit Enter (to skip messages and enter new virtual terminal. Everything looks the same, but u're actually in a different terminal compared to the original terminal
          • dietpi@DietPi:~$  cd freqtrade
          • dietpi@DietPi:~$  source .env/bin/activate
          • (.env) dietpi@DietPi:~$  freqtrade (to start this process, then detach, then reattach and come back to this exact same process still running)
          • Detach from the virtual terminal: Ctrl+A (to receive commands for Screen) > hit D (to "detach from" or get out (but not close) the virtual terminal) > observe a message that u have left a virtual terminal with a certain ID e.g. "[detached from 2761.pts-0.DietPi]". That virtual terminal is still running in the background.
            • Note: To close and shutdown any running program within your current virtual terminal, type "exit" (instead of Ctrl+A > D)
          • Close the tab (virtual terminal will continue to run in background)
          • Test by interacting with the telegram bot. Bot will reply. Means freqtrade is running.
      • How to reconnect to the same virtual terminal?
        • Run Chrome SSH extension
        • Click on the preset connection (red button)
        • dietpi@DietPi:~$  htop (to see processes running in memory)
          • Freqtrade is running in background because there is a 1 line command: "/home/dietpi/freqtrade/.env/bin/python3.7 /home/dietpi/freqtrade/.env/bin/freqtrade"
        • dietpi@DietPi:~$  screen -ls (to see list of screens running in background e.g.)
          • 2921.pts-0.DietPi (Detached)
          • 2761.pts-0.DietPi (Detached)
        • dietpi@DietPi:~$  screen -r 2921.pts-0.DietPi (to reconnect to a specific session)
      • Alternative to using Screen:
        • tmux
    • Run freqtrade using "systemD" service (equivalent to a system process in Window's Task Manager) (reference: https://gist.github.com/ilyasst/28bee4ee7ebb89e01284bc7e048346e9)
      • Why? If freqtrade jams due to electricity/wifi interruption, it detects the jam and will reboot itself (then connect to wifi and restart freqtrade). This is a better alternative to Screen (which doesn't reboot). 
      • Launch Chrome SSH, connect to RP
        • Error?
          • ssh: connect to host 192.168.0.168 port 22: Connection refused.. NaCl plugin exited with status code 255. ==> try again the next day (it'll work)
      • Ensure right freqtrade executable app is run by the correct python version
        • dietpi@DietPi:~$  nano /freqtrade/bin/freqtrade (didn't work) nano freqtrade/.env/bin/freqtrade (Works? yes)
        • Change the 1st line "#!/usr/bin/env python3" (python which came with the computer) to "#!/home/dietpi/freqtrade/.env/bin/python3" (python which came with freqtrade)
        • save
        • Add freqtrade strategy008 to systemD service. Admin to call software (systemctl)'s command "edit" to add a service
          • dietpi@DietPi:~$  sudo systemctl edit --force --full freqtrade008.service (Admin to run software's (systemctl) command "edit" to add a service called "freqtrade.service")
          • Paste the following service description into the opened nano file:
            • [Unit]
            • Description=Freqtrade008 Service
            • Wants=network.target
            • After=network.target

            • [Service]
            • ExecStartPre=/bin/sleep 10
            • ExecStart=/home/dietpi/freqtrade/.env/bin/python3 /home/dietpi/freqtrade/freqtrade/main.py trade -c /home/dietpi/freqtrade/config_008.json --strategy FREESTRAT2_008 --strategy-path /home/dietpi/freqtrade/user_data/strategies --userdir /home/dietpi/freqtrade/ --db-url sqlite:///trades008.dryrun.sqlite
            • Restart=always

            • [Install]
            • WantedBy=multi-user.target
          • Notes for service description above: 
            • (1) 3 sections: Unit, Service & Install. 
            • (2) "network.target" : tells system to allow network so can reach binance & telegram. 
            • (3) "ExecStartPre=/bin/sleep 10" to force freqtrade to wait 10 secs, to allow RP to boot completely (and have wifi enabled etc) before freqtrade runs.
            • (4) ExecStart: (Note: SystemD doesn't work within an activated virtual environment. So we launch the python version which meant for use with freqtrade (located in freqtrade's .env folder), to load the specific freqtrade file, tell freqtrade to trade, use a specific config file, tell location of strategies, use a specific strategy, with a specific user directory location, and specific database name (which must be located in root folder), and all paths must be absolute. So:
              • /home/dietpi/freqtrade/.env/bin/python3 /home/dietpi/freqtrade/freqtrade/main.py trade -c /home/dietpi/freqtrade/config_004.json --strategy BBRSI_004 --strategy-path /home/dietpi/freqtrade/user_data/strategies --userdir /home/dietpi/freqtrade/ --db-url sqlite:///trades008.dryrun.sqlite (works? yes )
            • (5) "Restart=always" : restart whenever crash occurs
        • Use filezilla to transfer files (config and strategy) from computer to RP
          • transfer "config_008.json" to "/home/dietpi/freqtrade"
          • transfer "strategy_008.py" to "/home/dietpi/freqtrade/user_data/strategies"
        • Enable the systemD service
          • dietpi@DietPi:~$  sudo systemctl enable freqtrade008.service 
        • Reboot RP to let systemD begin freqtrade automatically (as a system process)
          • dietpi@DietPi:~$  sudo reboot now
            • (R)econnect, (C)hoose another connection, or E(x)it? type "R".
            • wait 10+ seconds for telegram messages to appear (first ensure it's enabled in config)
        • Note: Upon rebooting, should start receiving telegram messages.
      • Interacting with SystemD (after freqtrade is already running as a system process)
        • Launch Chrome SSH, connect to RP
        • dietpi@DietPi:~$  sudo systemctl status freqtrade.service (to view status e.g. bot heartbeat etc)
          • errors
            • "ZeroDivisionError: float division by zero" : be prepared to manually sell off all coins, and delete exist database to begin a fresh one... works? Yes
        • dietpi@DietPi:~$  sudo systemctl stop freqtrade.service (stops running; telegram doesnt send any notification at all)
        • dietpi@DietPi:~$  sudo systemctl start freqtrade.service (starts running again; telegram sends usual starting msg e.g. "Status: running" & "Warning: Dry run is enabled. All trades are simulated."
        • dietpi@DietPi:~$  sudo systemctl restart freqtrade.service (If you update the config file, use this to force linux to restart)
        • dietpi@DietPi:~$  sudo systemctl disable freqtrade.service (disable the reboot if dont want to run freqtrade anymore, then reboot)
        • dietpi@DietPi:~$  sudo /opt/vc/bin/vcgencmd measure_temp (show core temperature of processor chip)
        • dietpi@DietPi:~$  sudo reboot now (MUST manually do this after the exchange maintenance period is over, in order to get the bot to resume trading)
        • To delete a service (e.g. freqtrade009.service):
          • dietpi@DietPi:~$ sudo systemctl stop freqtrade009.service
          • dietpi@DietPi:~$  sudo systemctl disable freqtrade009.service
          • dietpi@DietPi:~$  sudo rm /etc/systemd/system/freqtrade009.service
          • dietpi@DietPi:~$  sudo rm /etc/systemd/system/freqtrade009.service # and symlinks that might be related
          • dietpi@DietPi:~$  sudo rm /usr/lib/systemd/system/freqtrade009.service 
          • dietpi@DietPi:~$  sudo rm /usr/lib/systemd/system/freqtrade009.service # and symlinks that might be related
          • dietpi@DietPi:~$  sudo systemctl daemon-reload
          • dietpi@DietPi:~$  sudo systemctl reset-failed
          • dietpi@DietPi:~$ sudo rm /trades009.dryrun.sqlite
      • Self note: How to change a given bot's strategy from 001 to 002 on RP:
        • If bot is still actively trading using 001:
          • Telegram app: "/stopbuy" (stops buy new coins, continues sell existing coins)
            • If emergency: "/forcesell all" (instantly sell all, regardless of profit)
          • Telegram app: "/status table" (check if finished selling all coins held)
          • Telegram app: "/stop" (stop 001)
        • Backend (Chrome SSH): 
          • dietpi@DietPi:~$  sudo systemctl stop freqtradelive.service (stops running; telegram doesnt send any notification at all)
          • dietpi@DietPi:~$  sudo systemctl disable freqtradelive.service (disable rebooting with 001
            • see msg: "Removed /etc/systemd/system/multi-user.target.wants/freqtradelive.service."
            • strategy is removed from memory (wont appear when use "htop")
        • Upload 002's config and strategy files
          • config_002_LIVE.json
          • strategy_002_LIVE.py:
          • Use filezilla to upload to same folder level as 001 files
        • Database:
          • If you want to start afresh with empty database, then delete existing database in RP's root folder (e.g. "/tradesv3.dryrun.sqlite")
            • dietpi@DietPi:~$ sudo rm /tradesv3.dryrun.sqlite
            • This is the equivalent to: (1) Go up 2 levels, from "dietpi@DietPi:~$" to "dietpi@DietPi:/$" ; then (2) Delete using admin privileges: "dietpi@DietPi:/$ sudo rm tradesv3.dryrun.sqlite"
          • Otherwise you can continue using the existing database (add new 002 trades to the existing 001 trades).
        • Backend (Chrome SSH): 
          • dietpi@DietPi:~$  sudo systemctl edit --force --full freqtradelive.service (Admin to run software's (systemctl) command "edit" to add a service called "freqtradelive.service")
          • Update the highlighted config filename and strategy name as follows:
            • ExecStart=/home/dietpi/freqtrade/.env/bin/python3 /home/dietpi/freqtrade/freqtrade/main.py trade -c /home/dietpi/freqtrade/config_002_LIVE.json --strategy strategy_002_LIVE --strategy-path /home/dietpi/freqtrade/user_data/strategies --userdir /home/dietpi/freqtrade/
          • Save & exit
          • dietpi@DietPi:~$  sudo systemctl enable freqtradelive.service 
            • see msg: "Created symlink /etc/systemd/system/multi-user.target.wants/freqtradelive.service → /etc/systemd/system/freqtradelive.service."
          • dietpi@DietPi:~$  sudo reboot now (required, to load 002 into memory)
            • select "R"econnect
            • wait 10+ secs for telegram messages to appear (ensure allowed in config file)
    • Security notes
      • Not advisable to connect to RP from external wifi networks (google "port forwarding [your router model]" to do this). This opens your router's protective door, which attackers anywhere in the world can scan for (using bots) and attempt to takeover your RP.
      • If you absolutely need to change files while you are outside home, connect to your home PC remotely (e.g. use teamviewer), then connect to RP via Chrome SSH.
      • Recommended process flow
        • 1. Main PC: run ubuntu OS, then run VM, test different freqtrade strategies (including optimisation) & configs. Test each strategy for 1-2 days. Use SCREEN (bcos only 2 commands, & runs in bg). If strategy is good (optimised with hi sharpe ratio, did walkforward, did dryrun, did live-run with small money, and results are as desired), then transfer to RP.
        • 2. RP: Filezilla to transfer strategy & config file. Setup freqtrade to run via systemD (once need to do this once); i.e.:
          • "sudo sysmtectl edit --force --full freqtrade.service"
          • Paste the code and edit the strategy name (i.e. "DefaultStrategy"). Then run RP for weeks/months...
    • Backup RP's SD card
      • Why? RP shortcircuited (spilt water onto RP); used poor quality SD card; removed SD card from RP without shutting down the RP properly first.
      • Backup few files: Use Filezilla to transfer the files from RP to laptop. Useful for files u can see (e.g. config file, strategy file), but you will miss an entire hidden partition in the SD card.
      • How to safely remove SD card from RP and make a backup of entire SDcard
        • Login into Chrome SSH extension
        • dietpi@DietPi:~$ sudo shutdown now > press "X" to exit (shutdown RP, won't reboot, LED remains lighted red constantly(not blinking); can unplug power supply)
        • remove microSD card from RP > insert into SD card adapter > plug SD card adapter into laptop
        • Download & install Win32 Disk Imager https://thepihut.com/blogs/raspberry-pi-tutorials/17789160-backing-up-and-restoring-your-raspberry-pis-sd-card (enables u to see all files in SD card, copy them, and make the backup easy to restore in future)
          • Image file (select destination path to save the image file e.g. C:\downloads\rp-freqtrade.img)
          • Device (select source path to get image from) --> select any of the 2 partitions (i.e. "boot" (e.g. E:) or "usb drive" (e.g. F:)).
          • click "Read". Wait about 10 minutes. Image will be written to destination path.
        • If need to restore the saved image to the SD card, use balenaEtcher:
          • brand new SD card -> insert into adapter--> into into laptop
          • Select image (select image file created earlier i.e. C:\downloads\rp-freqtrade.img)
          • Select drive containing brand new SD card
          • click "Flash!" button.





    Wednesday, December 9, 2020

    Freqtrade Notes

    • Normal dist - big changes from mean are uncommon
    • Fat-tail dist - exists in financial markets, where big changes (from mean) have hi probabilities
    Generate a basic strategy
    • Use a chart to generate an idea
      • https://www.tradingview.com/chart/RARYjeai/
      • Insert some details at topright panel:
        • Symbol search: BINANCE:BTCUSDT
        • Time interval: 1 hr
        • Chart style: Candles
        • Indicators: Bollinger Bands (BB). Doubleclick to setup:
          • Length: 20 (no. of days used to calc moving average)
          • StdDev: 2
        • Indicators: Relative Strength Index (RSI). Doubleclick to setup:
          • Length: 14 (no. of past candles)
    • Observe. Tendency is price at lower BB will return to avg BB, and price at higher BB will return to avg BB.
    • So, the basic idea is to buy when price = lower BB (at time T1), and sell when price = avg BB or above (at time T2 > T1). But, sometimes BB can keep trending down, and no chance to sell at a higher price. (It's oversold, and continue being oversold for a long time). How to buy when BB stops going down and begin its trend up? 
    • When BB is about to begin trending up, it represents a change from "oversold" to "overbought". 
    • RSI looks at the past 14 candles. The bigger the green candles are relative to the red candles, the more "overbought" it is. The bigger the red candles are relative to the green candles, the more "oversold" it is. RSI takes a value between 0 (extremely oversold) and 100 (extremely overbought). RSI between 30 and 70 is desirable (stable). Oversold is 0-30, overbought is 70-100. When to buy? When it's not being oversold (i.e. when RSI >30). (When RSI is low e.g. 20... thinking is: hey something bad's going on right now, best not to buy now)
    • So, price hits lower BB... Is it a good time to buy? BB still looks like it's still falling. When will BB start rising? Check RSI. If RSI<30, then BB's are still falling. If RSI>30, then BB are potentially trending up, so, buy. Summary: If (price < lower BB) AND (RSI>30), then buy.
    • When to sell? If (price > avg BB), then sell.
    • So, basic strategy:
      • Buy if (price<lower BB) AND RSI>30
      • Sell if (price>avg BB)
    • Later, to optimise the strategy:
      • Buying... Lower BB: use how many SD ? (note: bigger SD means fewer but more profitable trades)
      • Buying... RSI: is 30 the best value? maybe 20? maybe 40?
      • Selling... use avg BB? could we use 1 SD below avgBB ? or 2 SD above avgBB?
    Automate to what Extent?
    • If you don't have time to watch the market, then fully automate the buy & sell decisions.
    • If you have time to read news that affects the fundamentals (vs technical), u cld automate to help u time the entry & exit.
    Code the Strategy
    • 1. Start VM (Oracle VM VirtualBox > Start a saved machine)
    • 2. In Ubuntu, open terminal (Ctrl+Alt+T)
    • 3. Activate the virtual environment
      • user1@computer1:~$ cd freqtrade
      • user1@computer1:~/freqtrade$ source .env/bin/activate
    • 4. Load Atom within freqtrade folder
      • (.env) user1@computer1:~/freqtrade$ atom .
    • 5. Duplicate the default strategy file and rename it e.g. bbrsi.py (you'll be working on this new file)
      • freqtrade\freqtrade\strategy\default_strategy.py > RHS click >duplicate> rename as "bb_rsi.py"
    • 6. In the new strategy file (bb_rsi.py), change the class name:
      • "class DefaultStrategy(IStrategy):" to "class BBRSI(IStrategy):"
    • 7. Tell the bot where to find the strategy file (bb_rsi.py) and strategy class (BBRSI) in the init file:
      • freqtrade\freqtrade\strategy\__init__.py
      • Duplicate the line "from freqtrade.strategy.default_strategy import DefaultStrategy"
      • Change, in the new line: "default_strategy" to "bbrsi", and "DefaultStrategy" to "BBRSI".
      • So now u have "from freqtrade.strategy.bbrsi import BBRSI"
    • 8. Minimal ROI: Tells the bot to sell a coin after a certain time if unable to detect a sell signal. It works independently of the sell signal. Default values (before optimization) are:
      • minimal_roi = {
        • "40": 0.0,  
        • "30": 0.01,
        • "20": 0.02,
        • "0": 0.04   
        }
      • Note: 0 to <20th min, sell if at least 4% profit, 20th to <30th min sell if at least 2% profit, etc.
    • 9. Stop Loss: Default value (before optimization) is 10%:
      • stoploss = -0.10
    • 10. Ticker Interval: 1h ("1m" gets many more trades compared to "1h")
      • ticker_interval = '1h'

    11. Define all indicators used in the "populate_indicators" function. This time we only need #RSI and #BollingerBands, delete all else.

    • def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:   
    •         # RSI
    •         dataframe['rsi'] = ta.RSI(dataframe)
    •         # Bollinger bands
    •         bollinger1 = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=1)
    •         dataframe['bb_lowerband1'] = bollinger1['lower']
    •         dataframe['bb_middleband1'] = bollinger1['mid']
    •         dataframe['bb_upperband1'] = bollinger1['upper']
    •         bollinger2 = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2)
    •         dataframe['bb_lowerband2'] = bollinger2['lower']
    •         dataframe['bb_middleband2'] = bollinger2['mid']
    •         dataframe['bb_upperband2'] = bollinger2['upper']
    •         bollinger3 = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=3)
    •         dataframe['bb_lowerband3'] = bollinger3['lower']
    •         dataframe['bb_middleband3'] = bollinger3['mid']
    •         dataframe['bb_upperband3'] = bollinger3['upper']
    •         return dataframe
    12. Buy decision: edit the "populate_buy_trend" function. Buy if RSI>30 AND closing price < lowerBB
    • def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:    
    •         dataframe.loc[                      
    •             (                               
    •                 (dataframe['rsi'] < 35) &   
    •                 (dataframe['close'] < dataframe['bb_lowerband3'])
    •             ),
    •             'buy'] = 1                      
    •         return dataframe
    13. Sell decision: edit the "populate_sell_trend" function. Sell if closing price > avg BB
    • def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:   
    •         dataframe.loc[
    •             (                                                                   
    •                 (dataframe['rsi'] > 81) &
    •                 (dataframe['close'] > dataframe['bb_middleband1'])
    •             ),
    •             'sell'] = 1                                             
    •         return dataframe
      Setup the config file
      1. freqtrade/config.json 
      2. Config file is more general, strategy file is more specific. If an identical parameter is found in both files, the bot will adopt parameters as per the specific strategy file
      3. Set the bot to do up to 15 trades simultaneously, using 0.01 BTC per trade (smallest allowed by the exchange). Cos when going live, we will have 0.15 BTC to trade. Display profit/loss in USD. Ticker interval was agreed at 1hour 
      • "max_open_trades": 15,
      • "stake_currency": "BTC",
      • "stake_amount": 0.01, (smallest amount allowed by the exchange)
      • "fiat_display_currency": "USD",
      • "ticker_interval" : "1h",
      • "dry_run": true,           (we're backtesting i.e. running the strategy on past data)
      4. Backtest using as many coins as possible
      • "pair_whitelist": [

        "ADA/BTC",

        "AAVE/BTC",

        "AION/BTC",

        "AMB/BTC",

        "APPC/BTC",

        "BAND/BTC",

        "BLZ/BTC",

        "BNB/BTC",

        "BRD/BTC",

        "BZRX/BTC",

        "DASH/BTC",

        "EOS/BTC",

        "ETC/BTC",

        "ETH/BTC",

        "FIL/BTC",

        "HARD/BTC",

        "ICX/BTC",

        "KMD/BTC",

        "LINK/BTC",

        "LSK/BTC",

        "LTC/BTC",

        "MANA/BTC",

        "NEO/BTC",

        "ONT/BTC",

        "ORN/BTC",

        "POWR/BTC",

        "PPT/BTC",

        "QLC/BTC",

        "RDN/BTC",

        "REN/BTC",

        "RLC/BTC",

        "SNGLS/BTC",

        "STX/BTC",

        "SYS/BTC",

        "THETA/BTC",

        "TRX/BTC",

        "UNI/BTC",

        "VIA/BTC",

        "WAVES/BTC",

        "WBTC/BTC",

        "WTC/BTC",

        "XEM/BTC",

        "XLM/BTC",

        "XMR/BTC",

        "XTZ/BTC", 

        "XRP/BTC",

        "YFI/BTC",

        "ZEC/BTC",

        "ZIL/BTC"

                ],

      5. "Experimental" section: Old version of freqtrade would sell to achieve a certain profit. New version allows to sell when a sell signal is detected. So, turn on "use_sell_signal"; turn off  Sell only when achieve profit. 
      • "experimental": {
      •         "use_sell_signal": true,
      •         "sell_profit_only": false,
      •         "ignore_roi_if_buy_signal": false
      • },
      Quick check to ensure basic strategy code works (not backtesting yet)
      1. Run freqtrade (main.py) with specific config file (-c) and specific strategy (-s) on the top 50 pairs with most volume (--dynamic-whitelist) on the exchange. 
      (.env) user1@computer1:~/freqtrade$ python3 ./freqtrade/main.py -c config.json -s BBRSI  --dynamic-whitelist 50
      2. What's happening: Each round, the bot will try to find buy triggers among the top 50 coins (with most volume) on the exchange. For coins already bought, bot will try to find sell signal.
      3. This command is just to ensure code works without errors. If code appears to be running, hit Ctrl+C to stop it, and proceed to next section. To do backtest (and optimization), must do it on the coins in the pair_whitelist. Next: Backtesting.

      Backtesting
      1a. Execute a backtest (backtesting) by getting new data about the pairs in the pairs_whitelist (--refresh-pair) like this, and save backtest results to user_data/backtest/backtest-result.json:
      (.env) user1@computer1:~/freqtrade$ python3 ./freqtrade/main.py -c config.json -s BBRSI backtesting --refresh-pair --export trades --export-filename user_data/backtest_data/backtest-result.json
      1b. Execute a backtest (backtesting) using existing pairs data (assuming it was downloaded earlier)
      (.env) user1@computer1:~/freqtrade$ python3 ./freqtrade/main.py -c config.json -s BBRSI backtesting

      2. Notes:
      • If first time running a backtest, binance will give you 30 days of data (u can dload it). The longer u work on it, the more data u can addon to your current dloaded data. Backtesting with more data results in better bot.
      • Warnings such as: "XXX/YYY" has missing frames ==> several reasons: coin didn't trade during that period, or exchange was under maintenance during that period.
      • Downloaded data (e.g. ETH_BTC-1h.json) is stored in /freqtrade/user_data/data
      • Inside the data file, the format is like this:
        • [1544536800000, 0.025903, 0.025942, 0.025825, 0.025872, 7481.045]
        • => [Unix timestamp, open, high, low, close, volume]
      3. Backtest results:
      ....
      2020-12-10 21:29:31,016 - freqtrade.optimize.backtesting - INFO - Measuring data from 2020-11-10T14:00:00+00:00 up to 2020-12-10T12:00:00+00:00 (29 days)..
      Result for strategy BBRSI
      ================================================== BACKTESTING REPORT =================================================
      | pair     |   buy count |   avg profit % |   cum profit % |   total profit BTC | avg duration   |   profit |   loss |
      |:---------|------------:|---------------:|---------------:|-------------------:|:---------------|---------:|-------:|
      | ETH/BTC  |          12 |          -0.85 |         -10.16 |        -0.00101655 | 19:35:00       |        5 |      7 |
      | LTC/BTC  |          17 |           0.64 |          10.94 |         0.00109497 | 11:11:00       |       13 |      4 |
      | ETC/BTC  |          14 |          -0.07 |          -0.94 |        -0.00009413 | 16:39:00       |        7 |      7 |
      | DASH/BTC |          10 |          -0.57 |          -5.72 |        -0.00057255 | 18:12:00       |        6 |      4 |
      | ZEC/BTC  |          18 |           0.92 |          16.65 |         0.00166632 | 13:00:00       |       12 |      6 |
      | XLM/BTC  |          13 |          -0.36 |          -4.70 |        -0.00047067 | 18:14:00       |        6 |      7 |
      | POWR/BTC |          15 |          -0.81 |         -12.12 |        -0.00121320 | 16:00:00       |        8 |      7 |
      | ADA/BTC  |          12 |          -1.22 |         -14.63 |        -0.00146494 | 17:35:00       |        5 |      7 |
      | XMR/BTC  |           9 |          -0.03 |          -0.31 |        -0.00003128 | 15:07:00       |        5 |      4 |
      | BNB/BTC  |          11 |          -0.92 |         -10.12 |        -0.00101252 | 17:11:00       |        3 |      8 |
      | QLC/BTC  |          17 |           0.20 |           3.36 |         0.00033610 | 13:21:00       |        9 |      8 |
      | KMD/BTC  |          13 |          -1.21 |         -15.76 |        -0.00157717 | 16:46:00       |        5 |      8 |
      | WTC/BTC  |          16 |          -0.17 |          -2.69 |        -0.00026907 | 14:56:00       |        9 |      7 |
      | TRX/BTC  |          15 |          -0.47 |          -7.12 |        -0.00071259 | 13:08:00       |        4 |     11 |
      | XRP/BTC  |           8 |          -1.77 |         -14.14 |        -0.00141558 | 21:45:00       |        2 |      6 |
      | EOS/BTC  |          13 |           0.43 |           5.62 |         0.00056288 | 12:28:00       |       10 |      3 |
      | XVG/BTC  |          44 |          -0.29 |         -12.79 |        -0.00128081 | 5:45:00        |       14 |     30 |
      | NEO/BTC  |          14 |           0.11 |           1.51 |         0.00015073 | 13:30:00       |        8 |      6 |
      | LINK/BTC |          12 |          -0.74 |          -8.92 |        -0.00089329 | 14:40:00       |        7 |      5 |
      | ONT/BTC  |          16 |           0.71 |          11.40 |         0.00114147 | 13:45:00       |       10 |      6 |
      | XEM/BTC  |          10 |          -0.12 |          -1.16 |        -0.00011656 | 18:12:00       |        5 |      5 |
      | VET/BTC  |          20 |           0.50 |           9.91 |         0.00099211 | 8:57:00        |        9 |     11 |
      | ICX/BTC  |          12 |          -0.03 |          -0.35 |        -0.00003467 | 13:20:00       |        7 |      5 |
      | TOTAL    |         341 |          -0.18 |         -62.25 |        -0.00623100 | 13:40:00       |      169 |    172 |
      ...

      Interpretation:
      • Backtest of strategy BBRSI was performed on 29 days worth of data on the pairs found in the "pair_whitelist" list
      • Over the 29 days
        • made a total 341 trades (covers both cases whereby (1) buy + sold (bcos detected sell signal); and (2) buy + forced sell (bcos ran out of days). Average profit per trade of -0.18% (therefore a loss). (Ideally, avg profit per trade > 0, then automated trading can grow your cumulative profit more quickly). Also, each trade took an avg 13h40m00s (so this is not a quick strategy, it takes hours to get in & out). 
        • 169 trades were profitable, 172 trades were losses. So, u won 49.6% and lost 50.4% of all trades. (Ideally u want a >50% success rate. e.g. 180 won, 161 lost. Automated trading helps ppl complete the losing trades, so that they can enjoy the overall gains).
      • The biggest winner was ZEC, with 16.65% cum profit. Biggest loser was KMD, with 15.76% loss
      ...
      ================================================== SELL REASON STATS ==================================================
      | Sell Reason   |   Count |
      |:--------------|--------:|
      | sell_signal   |     332 |
      | force_sell    |       9 |
      =============================================== LEFT OPEN TRADES REPORT ===============================================
      | pair     |   buy count |   avg profit % |   cum profit % |   total profit BTC | avg duration   |   profit |   loss |
      |:---------|------------:|---------------:|---------------:|-------------------:|:---------------|---------:|-------:|
      | ETH/BTC  |           1 |          -0.55 |          -0.55 |        -0.00005459 | 5:00:00        |        0 |      1 |
      | LTC/BTC  |           1 |          -0.20 |          -0.20 |        -0.00002000 | 6:00:00        |        0 |      1 |
      | ZEC/BTC  |           1 |           0.21 |           0.21 |         0.00002057 | 5:00:00        |        1 |      0 |
      | POWR/BTC |           1 |          -0.39 |          -0.39 |        -0.00003892 | 1:00:00        |        0 |      1 |
      | KMD/BTC  |           1 |          -0.55 |          -0.55 |        -0.00005481 | 5:00:00        |        0 |      1 |
      | WTC/BTC  |           1 |          -0.76 |          -0.76 |        -0.00007581 | 1:00:00        |        0 |      1 |
      | NEO/BTC  |           1 |           0.47 |           0.47 |         0.00004667 | 7:00:00        |        1 |      0 |
      | VET/BTC  |           1 |          -0.20 |          -0.20 |        -0.00002000 | 5:00:00        |        0 |      1 |
      | ICX/BTC  |           1 |          -0.01 |          -0.01 |        -0.00000083 | 10:00:00       |        0 |      1 |
      | TOTAL    |           9 |          -0.22 |          -1.98 |        -0.00019772 | 5:00:00        |        2 |      7 |
      ...
      Interpretation:
      • Out of the 341 trades (in the first table), bot sold 332 bcos detected sell signal, and forced sold 9 bcos ran out of days.
      • When the bot forced sold those 9 trades, it caused a change in cum profit of -1.98%. This contributed to the overall cum profit in first table of -62.25% (meaning the -62.25% already includes the effect of the forced sold 9 coins).
      4. For better visualization, plot a selected coin's price vs time, to see all the detected buy and sell signals
      Example: Use freqtrade's plotter (plot_dataframe.py) to see the strategy's (-s) Bb_Rsi buy/sell signals for the pair (-p) ZEC/BTC, generated by the indicators (bollingerbands & RSI). We choose the bollinger bands indicators to be on the main plot (--indicators1 bb_lowerband,bb_middleband,bb_upperband) and RSI to be on separate plot (--indicators2 rsi):
      (.env) user1@computer1:~/freqtrade$ python3 ./scripts/plot_dataframe.py -s Bb_Rsi  -p ZEC/BTC --indicators1 bb_lowerband,bb_middleband,bb_upperband --indicators2 rsi
      • If you get the error ModuleNotFoundError: No module named 'plotly' , then try to install plotly first like this: (.env) user1@computer1:~/freqtrade$ pip install plotly
      • If you get the error FileNotFoundError: [Errno 2] No such file or directory: 'user_data/backtest_data/backtest-result.json' , then see here https://github.com/freqtrade/freqtrade/issues/2081
      • The name of indicators (e.g. bb_lowerband) must be defined in the strategy (i.e. bb_rsi.py > class "Bb_Rsi" > def "populate_indicators" > dataframe['bb_lowerband'] = bollinger['lower'])
      • The BB shaded area (vs BB lines) are shown by default.
      • Resultant plot appears in FIREFOX
        • Shows all possible buy & sell signals. Note: (1) If bot already bought, it won't buy again (need to sell current position). (2) If bot already sold, it won't sell again (need to buy into a new position). (3) If "max_open_trades" (in config.json) has been achieved, then the bot won't buy more.
      • Admittedly, TradingView charts look & feel more usable. https://www.tradingview.com/chart/RARYjeai/
      Optimization
      • Why? 
        • You want a strategy where u can let the bot run by itself while u sleep peacefully at night
        • Maximizing profit without care for size of risk (volatility or StdDev) taken is nuts
        • You want to maximise the Sharpe Ratio (returns relative to risk)
        • If bot A makes 40% profit but faced 40% volatility, its SR=1. If bot B makes 10% profit by facing 1% volatility, its SR=10. We prefer to run bot B (SR is higher). Running bot A means it could buy or sell at a very unfavourable prices; it might be very profitable one night, but the next night might lose everything. By running bot B, u can give it $1m and it would return $1.1m safely.
      • Objective: Maximise the Sharpe Ratio by adjusting available parameters
      • Use hyperopt:
        • will change the value of 1 parameter (e.g. RSI=30) (among several available) and run an iteration on available price data. If iteration gives an improved Sharpe Ratio, then it will try to develop that result further by changing that same parameter (e.g. RSI=29); if iteration gave lousier result then it will go back to the previous node and try something else. Eventually it will move to the next parameter (e.g. BB's std dev=3) and run iterations, measure results and adjust (e.g. BB std dev=2).
        • Important to allow a sufficient number of iterations to test all parameters with all reasonable values, so that results will reach a "convergence" or a local minima (to obtain the best possible Sharpe Ratio with a specific value of RSI and BB)
        • Initial developer team's idea was to minimize: result (= trade_loss + profit_loss + duration_loss) (see old "calculate_loss" function which has been commented out)
        • In our case, we have modified the "calculate_loss" function to incorporate and calculate the annualized Sharpe Ratio
          • considers first and last day of trade, to calc annualized values (SR = (return/risk)*(365)^(1/2))
          • considers slippage
            • Backtest may get great results, but reality is: (1) when u buy, u may not get the price u want (2) your presence has an impact on the price; u're an extra buyer (3) when u buy, u don't buy at the last price; u buy at the ask price (33) when u sell, u don't sell at the last price but u sell at the bid price.
            • To address slippage, we deduct 0.05% of the profit from each trade. With this extra loss, we face better odds when trading live.
          • sharp_ratio = expected_average_return/np.std(total_profit)*np.sqrt(365) (a negative value bcos the original approach was a minimization function)
          • Then get the max SR by taking the negative of the result.
      • How to write the optimization:
        • Test only parameters and range of values that make sense.
        • [OPTIONAL READING] In the file default_hyperopt.py, what matters to us:
          • "populate_indicators" function must have same indicators as strategy file (bb_rsi.py)
          • in the "populate_buy_trend" function, define the rships like this:
            • #GUARDS AND TRENDS (must be true then only go to "#TRIGGERS")
              • if 'rsi-enabled' in params and params['rsi-enabled']:
              •                 conditions.append(dataframe['rsi'] < params['rsi-value'])
              • Note: indicator (rsi) is either below or above a certain value "rsi-value". This value is constantly changing over time.
            • #TRIGGERS
              • if 'trigger' in params:
              •                 if params['trigger'] == 'bb_lower':
              •                     conditions.append(dataframe['close'] < dataframe['bb_lowerband'])
              •                 if params['trigger'] == 'macd_cross_signal':
              •                     conditions.append(qtpylib.crossed_above(
              •                         dataframe['macd'], dataframe['macdsignal']
              •                     ))
              •                 if params['trigger'] == 'sar_reversal':
              •                     conditions.append(qtpylib.crossed_above(
              •                         dataframe['close'], dataframe['sar']
              • Note: triggered when closing price is below lowerBB
          • in the "indicator_space" function, define the values to test:
            • Integer(20, 40, name='rsi-value'),
              • Note: Test different integer values of 'rsi-value' between 20 and 40 (must make sense; don't give the entire range 0-100)
            • Categorical([True, False], name='rsi-enabled'),
              • Note: Turn on/off  the indicator, just to see how it affects SR
            • Categorical(['bb_lower', 'macd_cross_signal', 'sar_reversal'], name='trigger')
              • Note: Test each indicator and try to find out which indicator is the best trigger. Must match indicators in #TRIGGERS.
          • in the "stoploss_space" function:
            • Real(-0.5, -0.02, name='stoploss'),
              • Note: Try real values (vs integers) between -50% and -2%, to find the best stoploss value which maximises Sharpe Ratio. % of that particular trade (not percentage of your entire account).
          • in the "roi_space" function:
            • Integer(10, 120, name='roi_t1'), 
            • Integer(10, 60, name='roi_t2'),
            • Integer(10, 40, name='roi_t3'),
            • Real(0.01, 0.04, name='roi_p1'),
            • Real(0.01, 0.07, name='roi_p2'),
            • Real(0.01, 0.20, name='roi_p3'),
            • Note1: Find an integer btwn 10 and 120 minutes for time t1 
            • Note2: and find a real number btwn 1% and 4% as profit p1
            • Note3: which maximizes the SR
        • DOING THE OPTIMIZATION:
          • Duplicate freqtrade/freqtrade/optimize/default_hyperopt.py then rename to e.g. bbris_opt.py then save
          • Replace class name "DefaultHyperOpts" with your strategy name e.g. BBRSI_OPT
          • "populate_indicators" function
            • def populate_indicators(dataframe: DataFrame, metadata: dict) -> DataFrame:
            •         # RSI
            •         dataframe['rsi'] = ta.RSI(dataframe)
            •         # Bollinger bands
            •         bollinger1 = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=1)
            •         dataframe['bb_upperband1'] = bollinger1['upper']
            •         dataframe['bb_middleband1'] = bollinger1['mid']
            •         dataframe['bb_lowerband1'] = bollinger1['lower']
            •         bollinger2 = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2)
            •         dataframe['bb_upperband2'] = bollinger2['upper']
            •         dataframe['bb_middleband2'] = bollinger2['mid']
            •         dataframe['bb_lowerband2'] = bollinger2['lower']
            •         bollinger3 = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=3)
            •         dataframe['bb_upperband3'] = bollinger3['upper']
            •         dataframe['bb_middleband3'] = bollinger3['mid']
            •         dataframe['bb_lowerband3'] = bollinger3['lower']
            •         bollinger4 = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=4)
            •         dataframe['bb_upperband4'] = bollinger4['upper']
            •         dataframe['bb_middleband4'] = bollinger4['mid']
            •         dataframe['bb_lowerband4'] = bollinger4['lower']
            •         return dataframe
            • Notes: 
              • Want to find best RSI to buy and best RSI to sell (rsi). Hyperopt will automatically try different values to find the best RSIs.
              • When buying, is it best to buy at lowerBB of 1 SD? 2 SD? 3SD? etc?Later will store the results of "bb_lowerband1", "bb_lowerband2", "bb_lowerband3" and "bb_lowerband4" in a dictionary. Gotta tell hyperopt to manually change the values of SD.
          • "populate_buy_trend" function
            • def populate_buy_trend(dataframe: DataFrame, metadata: dict) -> DataFrame:
            •             conditions = []
            •             # GUARDS AND TRENDS
            •             if 'rsi-enabled' in params and params['rsi-enabled']:
            •                 conditions.append(dataframe['rsi'] > params['rsi-value'])
            •             # TRIGGERS
            •             if 'trigger' in params:
            •                 if params['trigger'] == 'bb_lower1':
            •                     conditions.append(dataframe['close'] < dataframe['bb_lowerband1'])
            •                 if params['trigger'] == 'bb_lower2':
            •                     conditions.append(dataframe['close'] < dataframe['bb_lowerband2'])
            •                 if params['trigger'] == 'bb_lower3':
            •                     conditions.append(dataframe['close'] < dataframe['bb_lowerband3'])
            •                 if params['trigger'] == 'bb_lower4':
            •                     conditions.append(dataframe['close'] < dataframe['bb_lowerband4'])

            •             dataframe.loc[
            •                 reduce(lambda x, y: x & y, conditions),
            •                 'buy'] = 1

            •             return dataframe
            • NOTES: RSI: want to find the best "rsi-value" for the buy rsi (rsi) (current "best" value = 30. Maybe there's a better value to be found).
          • "indicator_space" function
            • def indicator_space() -> List[Dimension]:
            •         return [
            •             Integer(5, 50, name='rsi-value'),
            •             Categorical([True, False], name='rsi-enabled'),
            •             Categorical(['bb_lower1', 'bb_lower2', 'bb_lower3', 'bb_lower4'], name='trigger')
            •         ]
            • NOTE: 
              • choose the best integer for "rsi-value". Try integers between 5 and 50. Why? bcos dowan to buy when extremely oversold(0) or overbought (>70).
              • choose best category for "rsi-enabled". Should RSI be enabled (true) or not (false)?
              • choose best category for "trigger"... i.e. pick the best from among 'bb_lower1', 'bb_lower2', 'bb_lower3', 'bb_lower4'
          • "populate_sell_trend" function
            • def populate_sell_trend(dataframe: DataFrame, metadata: dict) -> DataFrame:
            •             # print(params)
            •             conditions = []
            •             # GUARDS AND TRENDS
            •             if 'sell-rsi-enabled' in params and params['sell-rsi-enabled']:
            •                 conditions.append(dataframe['rsi'] > params['sell-rsi-value'])

            •             # TRIGGERS
            •             if 'sell-trigger' in params:
            •                 if params['sell-trigger'] == 'sell-bb_lower1':
            •                     conditions.append(dataframe['close'] > dataframe['bb_lowerband1'])
            •                 if params['sell-trigger'] == 'sell-bb_middle1':
            •                     conditions.append(dataframe['close'] > dataframe['bb_middleband1'])
            •                 if params['sell-trigger'] == 'sell-bb_upper1':
            •                     conditions.append(dataframe['close'] > dataframe['bb_upperband1'])

            •             dataframe.loc[
            •                 reduce(lambda x, y: x & y, conditions),
            •                 'sell'] = 1

            •             return dataframe
            • NOTE: 
              • Personally guru doesn't believe need RSI to sell, but just let bot decide as an exercise.
              • When selling, we just look at BB with 1 SD (bcos waiting to sell at upperBB with 3 SD is too greedy). So, test: (1) sell when closing price hit 1SD lowerBB i.e. "bb_lowerband1" (for a "buy cheapest, sell cheap" strategy), (2) sell when close price hit 1sd middleBB, (3) sell when close price hit 1sd upperBB.
          • "sell_indicator_space" function
            • def sell_indicator_space() -> List[Dimension]:
            •         return [
            •             Integer(60, 100, name='sell-rsi-value'),
            •             Categorical([True, False], name='sell-rsi-enabled'),
            •             Categorical(['sell-bb_lower1', 'sell-bb_middle1', 'sell-bb_upper1'], name='sell-trigger')
            •         ]
            • NOTE: (1) Find the best integer btwn 60 and 100 for "sell-rsi-value". Why? Just for the exercise. Usually be patient to buy-in, but wanna be quick (don't want to make it a hassle (i.e. wait for RSI) to get out. (2) Pick best category for sell-rsi-enabled: Use it (true) or don't use it (false) ? (3) choose best category for "sell-trigger"... i.e. pick the best from among 'sell-bb_lower1', 'sell-bb_middle1', 'sell-bb_upper1'.
          • "populate_buy_trend" and "populate_sell_trend" functions:
            • Copy both the entire functions from bbrsi.py and paste as the last part in bbrsi_opt.py
          • Finally, register your file-to-be-optimized in the init file:
            • /freqtrade/freqtrade/optimize/__init__.py
            • Duplicate line "from freqtrade.optimize.default_hyperopt import DefaultHyperOpts" so now u have 2 consecutive identical lines. Newer line:
              • replace "default_hyperopt" with your strat file i.e. "bbrsi_opt"
              • replace classname "DefaultHyperOpts" with yours i.e. BBRSI_OPT
          • Run the optimization:
            • Get custom opt (--customhyperopt) to run the optimization class called "BBRSI_OPT" (in "class BBRSI_OPT(IHyperOpt):"), and get "hyperopt" to iterate (-e) 100 times, onto a timerange (--timerange) that must be equal to that used in the backtest (20201110-20201216)
            • (.env) user1@computer1:~/freqtrade$ python3 ./freqtrade/main.py -c config.json --customhyperopt BBRSI_OPT hyperopt -e 1000
              • (Note: try run in batches of 500-1000 iterations; any higher tends to crash my PC somehow. Completing a hyperopt will save the results, & u can run the same command again which builds on those saved results. When a hyperopt crashes midway, u lose the results for that round.)
            • Note: Give it the biggest number of iterations to find the  best combination that maximises SR. E.g. Leave it to run overnight. Or while u go out. (100 is insufficient).
            • Hit Ctrl+S to pause
            • How to read, example:
              • 758/1000:    212 trades. Avg profit  0.35%. Total profit  0.00732600 BTC (73.1891Σ%). Avg duration 699.9 mins.. Loss -29.24738
              • ...
              • Note: 758th iteration (changed a parameter & ran 758th time). Made 212 trades. Total profit was +73.2% over 40 days (==> +668% profit/year). Sharpe Ratio = +29.2 (= -1 x -29.24738)
              • Note: Each dot "." is 1 iteration (or "epoch"). Bot only displays full description of iteration when it discovers an improvement (i.e. increase) in the Sharpe Ratio. If u specified thousands of iterations and all u see are dots after hundreds of iterations, u can hit Ctrl+C to end the process, and bot will print the best results obtained thus far (and u can use that).
              • 'rsi-enabled': False,
              • 'rsi-value': 26,
              • Note: Using the RSI to buy is not recommended (even if the recommended value was 26)
              • 'sell-rsi-enabled': True,
              • 'sell-rsi-value': 76,
              • Note: Using the RSI to sell is recommended. Recommended value is 76
              • 'sell-trigger': 'sell-bb_lower1',
              • Note: Best to sell when closing price hits lowerBB @ 1SD
              • 'stoploss': -0.18757341633907693,
              • Note: The best stoploss is -18%
              • 'trigger': 'bb_lower3'}
              • Note: The best buy trigger is when price hits lowerBB @ 3 SD
              • {   0: 0.1038762896661849,
              •     26: 0.07887892828698762,
              •     48: 0.013130689064203501,
              •     108: 0}
              • Note: [Above "ROI table" is a strategy based on exit time (unrelated to SR)]. To maximize profit: 0-25th minute: exit if u have at least 10.4% profit. If can't achieve that 10.4%, then in 26-47th minute, exit with at least 7.9% profit. If can't achieve that 7.9%, then in 48th-107th minute, exit with at least 1.3% profit. If cant achieve that 1.3%, then exit with at least 0% profit.
            • Saved iterations
              • Start a new strategy and do 100 iterations; the discoveries are saved here:
                • /freqtrade/user_data/hyperopt_results.pickle
                • /freqtrade/user_data/hyperopt_tickerdata.pkl
              • Continue with exact same strategy and do another 50 iterations, the bot will read the previous work (from the 2 pickle files above), then continue where it left off.
              • If u decide to change anything in the strategy, must delete the above 2 files (bcos the previous work done should not be referred any longer, bcos variables have changed). Then only run new iterations.
            • Once you've run sufficient iterations and happy with the results, copy them aside, example:
              • Best result:  
              • 212 trades. Avg profit  0.35%. Total profit  0.00732600 BTC (73.1891Σ%). Avg duration 699.9 mins.
              •     'rsi-enabled': False,
              •     'rsi-value': 33,
              •     'sell-rsi-enabled': True,
              •     'sell-rsi-value': 81,
              •     'sell-trigger': 'sell-bb_middle1',
              •     'stoploss': -0.4884759339579788,
              •     'trigger': 'bb_lower3'}
              • {   0: 0.14804935708164307,
              •     13: 0.04033164340222185,
              •     39: 0.01698340789831671,
              •     80: 0}
            • Results must make sense (because, it cld be true for this data, but may not work during live runs): Buy when closing price hits bb_lower3 (i.e. oversold) and rsi-value=33 (i.e. price is falling but drop rate is slowing) (==> buy at a low good price). Sell when closing price hits sell-bb_middle1 (i.e. middle band of 1 SD), and sell-rsi-value=81 (i.e. overbought, on a spike up). Strategy makes sense bcos buy at very cheap, and sell at average price.
          • Accuracy test: Does backtesting the strategy while using the optimized parameter values (e.g. RSI values, bollinger SD, stoploss, etc) give the same results (i.e. no. of trades, avg profit, total profit %, etc)?
            • Copy optimized values onscreen & paste into original strategy file (bbrsi.py)
              • ROI table (e.g. 20: 0.032379109428157866,)  --> shd appear in  "minimal_roi" dictionary as ("20": 0.032379109428157866,)
              • stoploss
              • Buy BB: 
                • 'trigger': 'bb_lower2' --> "populate_indicators" fn >>>  bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2)
              • Sell BB:
                • 'sell-trigger': 'sell-bb_middle1', --> "populate_sell_trend"  fn >>> (dataframe['close'] > dataframe['bb_middleband'])
              • Buy RSI:
                •     'rsi-value': 41, --> "populate_buy_trend" fn >>>  (dataframe['rsi'] >41)
              • Sell RSI:
                •     'sell-rsi-value': 96, --> "populate_sell_trend" fn >>> & (dataframe['rsi'] >96)
                • Note: entire line "& (dataframe['rsi'] >96)" added in manually
            • Save updated strategy file (bbrsi.py)
            • Execute the backtest to run in the same environment as hyperopt (i.e. DO NOT REFRESH PAIRS bcos will change the dataset that the hyperopt ran with earlier):
              • (.env) user1@computer1:~/freqtrade$ python3 ./freqtrade/main.py -c config.json -s BBRSI backtesting --export trades --export-filename user_data/backtest_data/backtest-result.json --eps --dmmp
              • Note: This will save the backtest results to user_data/backtest/backtest-result.json
            • Backtest should result in "TOTAL" row showing same number of trades and cum profit % as per results from the optimization process. If identical, this means strategy is working correctly, ready to go to next step (Sandbox: i.e. test strategy in live market using fake money).
              • Not identical? Check that, before starting optimization: 
                • (1) Ensure "trailing_stop": true in config.json. 
                • (2) The contents of the 3 functions (populate_indicatorspopulate_buy_trend and populate_sell_trend) should be identical word-for-word in "bbrsi.py" and "bbrsi_opt.py".
                • (3) Ensure when backtesting you used "--eps" and "--dmmp". "Enable position stacking" means allow bot to buy the same pair multiple times. "Disable max market positions" means disable the max open trades during a backtest.
      • Run strategy in Sandbox
        • Why? Let strategy trade using fake money, on live data which it has never seen before (out-of-sample data) to see if strategy still performs (i.e. are results similar to the backtest results of the optimized strategy?). Does it underperform due to overfitting? Let strategy complete 100 trades before deciding.
        • Command: run freqtrade, using a specific (-c) config file in the same freqtrade folder (config.json), using a specific strategy (-s) BBRSI, on the top 100 coin pairs on the exchange with most volume
          • (.env) user1@computer1:~/freqtrade$ python3 ./freqtrade/main.py -c config.json -s BBRSI --dynamic-whitelist 100
            • Note: A max of 100 pairs is still ok, not many will have low volume (i.e. hi chance of slippage).
            • If strategy involves frequent buy-sell within short span of time, better to avoid low liquidity coins (i.e. pick a lower number of pairs e.g. 50)
            • Slippage: u r an active member in the orderbook, so u may not get result as good as in the  backtest.
        • Once execute the command, u can monitor the telegram messages on phone:
          • Status: running
          • Warning: Dry run is enabled. All trades are simulated.
          • Exchange: binance
            • Stake per trade: 0.01 BTC
            • Minimum ROI: {xxxxxxxxxx}
            • Ticker interval: 1h
            • Strategy: BBRSI
          • Status: Searching for BTC pairs to buy and sell based on VolumePairList - top 100 volume pairs
        • Bot will keep looking for buy signals each round. Based on backtest results: data spanned 35 days, total trades=219 trades, so u expect an average of 6 trades per day.
        • When buy signal is found
          • Telegram:
            • Binance: Buying XXX/BTC with limit 0.00000xxx
            • (0.01 BTC, 36.594 USD)
          • Terminal:
            • Buy signal found: about to create a new trade with stake_amount: 0.01 BTC
            • Using Last Ask / Last Price
            • Sending rpc message: {xxxxxx} (tells binance)
            • Wallets synced
            • Found open order for Trade(id=1, pair=XXX/BTC, amount=xxx...)
            • Applying fee on amount for Trade(id=1, ....)
            • Updating trade (id=1)..
            • LIMIT_BUY has been fulfilled for Trade(id=1, ...) (the bot simulates a trade)
        • Telegram commands:
          • Hit the "/daily" button: see total profit per day for past (default) 7 days. To see past N days, type "/daily N".
          • Hit the "/profit" button:
          • Hit the "/balance" button: see coins remaining in your exchange's wallet
          • Hit the "/status" button: see "tradeID", pair name, when open (e.g. 2 mins ago), amount, open rate (i.e. buy price), close rate, current rate (i.e. current price), close profit (i.e. profit if sold the pair), current profit (%) (note: usually negative initially bcos u bought while price was dropping). Note: (1) In live mode, if a buy order was placed, but timeout bcos unable to fulfill the order, bot will cancel the current order (e.g. TradeID=8) and place a new buy order with updated id (TradeID=9). (2) In live mode, if OpenRate = CurrentRate, then CurrentProfit= trade fee (e.g. -0.20%).
          • Hit the "/status table" button: (nice table). In live mode, profit includes the trading fee. Status of open trades. See pairs which, (1) failed to fulfill any "sell signal", and also (2) did not fulfill the minimum ROI strategy.
          • Hit the "/performance" button: See all closed trades. Format: Pair, Profit%, Number of trades
          • Hit the "/count" button: see current open positions (e.g. 4), and max positions allowed (i.e. 15), total stake = 0.04 (=4 positions x 0.01BTC)
          • Hit the "/help" button: see all commands u can use
          • e.g. type "/forcesell 4": sell the pair with tradeID=4.
            • errors?
              • Error msg (use "sudo systemctl status freqtrade.service" to see error msg):
                • freqtrade.exceptions.TemporaryError: Could not get balance due to InvalidNonce. Message: binance {"code":-1021,"msg":"Timestamp for this reque st is outside of the recvWindow.
              • Do this:
                • sudo  apt-get update
                • sudo apt-get install ntpdate
                • sudo ntpdate ntp.ubuntu.com
          • Hit "/reload_config": reload config if any changes were made to config or strategy file
            • errors?
              • Telegram msg stuck at "Status: reload_config" ==> use "sudo systemctl status freqtrade.service" to confirm the error is "freqtrade.exceptions.TemporaryError: Could not get balance due to InvalidNonce. Message: binance {"code":-1021,"msg":"Timestamp for this reque st is outside of the recvWindow." ==> solution: "sudo ntpdate ntp.ubuntu.com"
        • Observe visually:
          • Open TradingView https://www.tradingview.com/chart/RARYjeai/ & view your pair
            • set e.g.: ARPA/BTC , 1h , BB@3sd
          • Buy occurs when a candle hits a lowerBB, but why did buy price>BB and not =BB ?
            • Look at order book (use e.g. https://www.binance.com/en/trade/ARPA_BTC > TradingView > 1h > insert indicators): Bought at last ask price. Means bought at higher price. This is slippage. When pairs are less popular => less buyers & sellers => spread is bigger => ask price is higher => slippage bigger.
            • Conclusion: Trade pairs with higher volumes so that spreads are smaller, to minimize slippage.
        • Note: When u have a strategy with good results (i.e. high Sharpe Ratio), don't think *i have to change things to see more trades done frequently*. It's performance that counts. BUT if you like to see more trades, just play with the time interval i.e. instead of using 1h candles, try using 15m candles.
        • Once results are good (i.e. similar no. of trades, profit % per period etc to the hyperopt), then u're ready to go live.
      • Run strategy live
        • config.json: change "dry_run": true, to "dry_run": false,
        • Minimise losses due to wide spreads:
          • Pairs with low volume will have larger spreads. 
          • Example: 
            • Ask price=0.00008150
            • Bid price= 0.00008130
            • Spread=20 satoshis (=8150-8130)
            • By default, when the bot buys at the last price, it assumes last price = ask price (i.e. the higher price), and will buy at 8150 
            • In the low volume example above, when bot buys at ask price, it loses 0.24% (=(8130-8150)/8130 x 100%)
          • Solution: Buy at the bid (lower) price
            • Cons: price might go up and u miss the purchase, BUT
            • Pros: when u consider the 0.24% for all the trades done... it adds up n u get overall more profit. Since it's automated, u can let bot do it (u don't have to be there at the computer patiently waiting...). So, try to buy at bid price to get more profit.
          • How?
            • config.json > bid strategy (i.e. your bot's buying strategy) > use_order_book: change false to true
            • This change is not done during sandbox mode bcos u don't have actual bid & ask prices to work with. Only do it for live mode. In sandbox mode, assumption is bot buys at the worst price (high ask price), and sells at the worst price (low bid price)
            • config.json > bid strategy ask_last_balance: change 0.0 to 1.0 (from "buy at (higher) ask price" to "buy at (lower) bid price")
          • Note about: config.json > ask strategy (i.e. your  bot's selling strategy)
            • Don't use order book. Why? Order book adds another layer of decisions. We want to be able to get out of the trade quick if possible. (Careful to buy, quick to sell)
        • Prepare starting capital
          • Binance > Wallet > Fiat & Spot https://www.binance.com/en/my/wallet/account/main
          • Start with 0.15109228 BTC in wallet (ideally 0.15 BTC)
          • Have e.g. 12.81661160 BNB (=0.03357568 BTC) (Binance gives discount for trading fee if we keep BNB in wallet; Binance deduct from this BNB)
          • Note starting total BTC value = 0.18466796 BTC (=0.15109228+0.03357568)
        • Run live
          • (.env) user1@computer1:~/freqtrade$ python3 ./freqtrade/main.py -c config.json -s BBRSI --dynamic-whitelist 100
        • Once execute the command, can monitor the telegram messages on phone:
          • Status: running
          • Exchange: binance (No more "Dry run is enabled. All trades are simulated.")
          • etc
        • Telegram messages shd correspond to the exchange's orders. 
        • See open orders here: Binance > Orders >  Spot Orders (https://www.binance.com/en/my/orders/exchange/openorder)
        • To see your open (i.e. unfulfilled) order in the order book:
          • Binance > Trade > Advanced (https://www.binance.com/en/trade/BTC_USDT?layout=pro) > enter the crypto ticker > your open order shd have a yellow arrow in the order book. If you did the "Minimise losses due to wide spreads" above, the arrow should point at the bid price.
        • To close all positions, normally just type "forcesell" button in Telegram. If accidentally hit "/stop" button in Telegram, then goto https://www.binance.com/en/trade/BTC_USDT?layout=pro and click "Cancel" on each open (unfulfilled) buy/sell order.
        • In case of hardware failure (e.g. blackout), u can just restart the bot and freqtrade will continue where it left. Consider using spare PC backup battery, Raspberry Pi, VPS, etc.
        • Calculate your absolute performance:
          • Note the starting total BTC value (before began trading) (e.g. 0.18537136 BTC)
          • https://www.binance.com/en/my/wallet/account/main > Get ending total BTC value (e.g. 0.19820787 BTC)
          • Profit = (0.19820787/0.18537136-1)x100% = 6.9% over 4 days => amazing
        • Extra note: U can use your BTC capital more efficiently by changing the "stake" amount, so that all BTC in the wallet is unused (to avoid small fractions of BTC sit unused in the exchange wallet). e.g. instead of 0.01BTC per trade, try use 0.015 BTC. Maximise BTC used to maximise return.
      Walk-forward Analysis
      • Usual way to get better results: 1. Strategy that makes sense 2. LARGE sample size (many different pairs)(& more historical prices per pair) 3. WFA 4. Sandbox then Live (using small money) 
      • WFA
        • Aim: To avoid overfitting. Example: Split past data into 2 parts: for training the strategy, and for testing the trained strategy. If the strategy still yields good results, then its not overfitted.
        • Lets say u have 3 months of data: Jan, Feb, Mar
        • Jan-Feb: use this data to get optimized strategy ("in-sample data")
        • Mar: use your optimized strategy to perform a backtest on Mar data ("out-of-sample data")
      • How
        • Open data file e.g.: freqtrade\user_data\data\binance\XLM_BTC-1h.json
        • Get start date: 
          • See 1st row > copy the first 10 digits of the Unix timestamp e.g. 1544310000
          • Convert to normal timestamp: https://www.unixtimestamp.com/
          • Get normal time: e.g. 12/08/2018 @ 11:00pm (UTC) ==> Day 1 is Dec 8th, 2018
        • Get end date:
          • Do the same for the last row: e.g. 1549846800 
          • ==> 02/11/2019 @ 1:00am (UTC) ==> Last day is Feb 11th, 2019
        • Split the days
          • Total days =  65 (can use https://www.timeanddate.com/date/duration.html)
          • Lets optimise using first 2/3 of total   = approx 40 days (so, in freqtrade timerange format: 20181208-20190118)
          • Lets walkforward using last 1/3 of total = approx 25 days (so: 20190119-20190211)
        • Note: If you have a bot running a sandbox on one terminal, u can always open a new terminal to run something else: 
          • 1. New terminal: Ctrl+Alt+T
          • 2. Activate virtual environment
            • user1@computer1:~$ cd freqtrade
            • user1@computer1:~/freqtrade$ source .env/bin/activate
          • 3. Then proceed to do the other activity.
        • Run the optimization on the first 2/3 "in-sample" data:
          • user1@computer1:~/freqtrade$ python3 ./freqtrade/main.py -c config.json --customhyperopt BBRSI_OPT hyperopt -e 1000 --timerange=20181208-20190118
        • Once optimised results appear, insert them into the strategy (i.e. bbrsi.py): e.g. minimal roi, stopless, buy signals, sell signals. Backtest against the SAME "in-sample" data if u want:
          • (.env) user1@computer1:~/freqtrade$ python3 ./freqtrade/main.py -c config.json -s BBRSI backtesting --export trades --export-filename user_data/backtest_data/backtest-result.json --eps --dmmp --timerange=20181208-20190118
        • Run the backtest on the last 1/3 "out-of-sample"data:
          • user1@computer1:~/freqtrade$ python3 ./freqtrade/main.py -c config.json -s BBRSI backtesting --export trades --export-filename user_data/backtest_data/backtest-result.json --timerange=20190119-20190211
        • Compare results
          • cumulative profit % still positive? => can do sandbox => go live.
      Get More Strategies
      • Why? Build new strategies by learning from other strategies, borrow from them, combine indicators, etc.
      • Google "github freqtrade strategies": 
      • Select a strategy (e.g. AverageStrategy.py
        • Summary: Uses macd. When MA-ST (=8 periods) cross above MA-LT (=21 periods) => buy. Vice versa.
        • > click RAW button (code has no hidden html) > remember the strategy's class e.g. "AverageStrategy" in "class AverageStrategy(IStrategy):"  > select all > copy
        • Make new strategy file: freqtrade\freqtrade\strategy\default_strategy.py > RHS click >duplicate> rename as "macrossover.py" > paste into macrossover.py > save
        • Register new strategy file: freqtrade\freqtrade\strategy\__init__.py > Duplicate the line "from freqtrade.strategy.default_strategy import DefaultStrategy" > Change, in the new line: "default_strategy" to "macrossover", and "DefaultStrategy" to the class name "AverageStrategy". > So now u have "from freqtrade.strategy.macrossover import AverageStrategy"
      • Take it on a test run (sandbox mode)
        • Ensure: config.json > "dry_run": true
        • (.env) user1@computer1:~/freqtrade$ python3 ./freqtrade/main.py -c config.json -s AverageStrategy --dynamic-whitelist 100
      • Try to get a feel for the strategy, understand what it's doing in the background.
      • Try to backtest it, optimise it, do walkforward analysis, sandbox, live.
      Edge Positioning
      • Why? Get a possible extra edge in the strategy by optimizing the stoploss for each pair. Great if you have many trades for that pair (i.e. LOTS of data for that pair). Not good if you have only a few trades for that pair (e.g. <5 trades done for that pair in a backtest), because then it's overfitting the stoploss for that pair.
      • How?
        • Setup
          • Enable edge. Perform edge once every 3600 seconds. When it runs, it performs calculations based on past 7 days. Edge will update your strategy's stoploss dynamically when running sandbox or live mode.
          • Config file:
            • "max_open_trades": -1,
            • "edge": {
            •         "enabled": true,
            •         "process_throttle_secs": 3600,
            •         "calculate_since_number_of_days": 7,
            •         "allowed_risk": 0.02,
            •         "stoploss_range_min": -0.01,
            •         "stoploss_range_max": -0.90,
            •         "stoploss_range_step": -0.01,
            •         "minimum_winrate": 0.0,
            •         "minimum_expectancy": 0.00,
            •         "min_trade_number": 1,
            •         "max_trade_duration_minute": 1440,
            •         "remove_pumps": false
            •     },
        • Run edge module, with specific strategy (--strategy BBRSI), on a specific period (--timerange=20181110-20181113)
          • freqtrade edge --strategy BBRSI --timerange=20201015-20201220


      Impact of Cheap Coins on Backtests
      • Don't use cheap coins in backtests. Leads to biased "Total cum profit %" in backtest, which lead to wrong decisions.
      • Why?
        • "Regular price" coin e.g. DASH/BTC
        • "Cheap" coin e.g. HOT/BTC.  (those worth single- or double-digit satoshis)
          • See order book: https://www.binance.com/en/trade/HOT_BTC
          • If a trade sees price change from 0.00000003 BTC to 0.00000004 BTC (i.e. from 3 satoshi to 4 satoshi), then profit is +33.3% per trade
          • Assuming 100 trades in the backtest, then cum profit % = +3333%
        • In a backtest containing many "regular priced" coins and 1 "cheap" coin, if somehow most regular priced coins resulted in a negative cum profit % and 1 cheap coin gave +3333%, then TOTAL cum profit % becomes positive and u would have decided to take the strategy live and get hammered with losses from the regular priced coins. U wont be able to trade the cheap coins effectively bcos there are many professional traders who just focus on buy/sell these coins to profit from this alllll day, and if u r buying at bid (i.e. lower) price to save costs, it can take forever to get your buy order filled (maybe just once in 24 hours).
      • So, AVOID the cheap coins: https://coinmarketcap.com/ > sort by price (lowest first) > identify coins with 1-2 digit satoshis.
        • ONE/BTC
        • ETN/BTC
        • QKC/BTC
        • IOST/BTC
        • IOTX/BTC
        • XVG/BTC
        • TMTG/BTC
        • ANKR/BTC
        • REV/BTC
        • RVN/BTC
        • CHZ/BTC
        • TFUEL/BTC
        • VET/BTC
        • FTM/BTC
        • MATIC/BTC
        • RSR/BTC
        • DGB/BTC
        • HOT/BTC
        • YOYOW/BTC
      UPDATE TO LATEST VERSION OF FREQTRADE
      • After learning & understanding old version of freqtrade, highly recommended to get latest version. 
      • Auto-updates tend to cause issues. If this happens, best to get the very latest versions and reinstall:
        • VirtualBox-6.1.17-141968-Win.exe (https://www.virtualbox.org/wiki/Testbuilds)
        • ubuntu-20.04.1-desktop-amd64.iso (https://ubuntu.com/download/desktop)
        • VBoxGuestAdditions_6.1.17-141968.iso ((https://www.virtualbox.org/wiki/Testbuilds)(install instructions: https://itsfoss.com/virtualbox-guest-additions-ubuntu/)
      • How to get latest version
        • Create virtual machine: virtualbox.org > download VirtualBox > install > New > Name(VM1)> Type(Linux) > Version(Ubuntu 64-bit) > NEXT > MemorySize(8000mb) > Create virtual harddisk now > VirtualBox disk Image > Dynamically allocated > 10GB > CREATE
        • Install OS: Ubuntu.com > download ubuntu (e.g. 20.04.1 Desktop LTS) > RHS click on VM1 > Settings > Storage > ControllerIDE(empty)> choose virtual optical disk > add disk image > select downloaded file > CHOOSE > Ok > click "START" to bootup the virtual machine > "Install Ubuntu" (once installed, can delete the .iso file) > normal installation + download updates during installation + install 3rd party software > erase disk & install ubuntu > Install Now > YourName(FirstLast) + ComputerName(computer1) + Username(user1) + Pwd(xx) + LoginAutomatically > go> installs... > Restart > "Pls remove installation medium">(remove cd by: Devices>OpticalDrive>select downloaded file > Remove disk from virtual drive) > Force unmount > ENTER.
          • Ignore any software update request from the "Software Updater" popup.
        • Install Guest Additions (to enable fullscreen, copypaste, etc): 
          • $ sudo add-apt-repository multiverse
          • Try this first (bcos latest virtualbox (as at 12jan2021) uses kernel 5.8 which has bug. Must switch back to kernal 5.4):
            • Start VM and immediately hit ESC twice hold left shift to access GRUB menu
            • Grub menu: "Advanced options for Ubuntu" > "Ubuntu, with Linux 5.4.0-42-generic"
          • Try next: 
            • Ubuntu > firefox > https://www.virtualbox.org/wiki/Linux_Downloads > download "virtualbox-6.1_6.1.16-140961~Ubuntu~eoan_amd64.deb" into Downloads folder
            • Terminal:
              • cd Downloads
              • Uninstall old dkms (sudo apt-get remove --auto-remove dkms)
              • Remove all virtualbox* pockets ($ sudo apt-get purge virtualbox*)
              • Launch install in terminal ($ sudo dpkg -i virtualbox-6.1_6.1.16-140961_Ubuntu_eoan_amd64.deb)
          • $ sudo apt install virtualbox-guest-dkms virtualbox-guest-x11
          • $ sudo reboot
          • Installation instructions https://itsfoss.com/virtualbox-guest-additions-ubuntu/
        • Install Python3.7+: (required by freqtrade) 
          • Ubuntu 20.04 has Python 3.8 installed by default
          • python3 --version
        • Install pip for Python 3.8 (required by freqtrade) (~5minutes)
          • sudo apt update
          • sudo apt install python3-pip
            • Errors?
              • debconf: delaying package configuration, since apt-utils is not installed
              • Selecting previously unselected package libfftw3-double3:armhf.
              • dpkg: unrecoverable fatal error, aborting:
              •  files list file for package 'gcc-8' is missing final newline
              • E: Sub-process /usr/bin/dpkg returned an error code (2)
            • Solution:
              • sudo rm /var/lib/dpkg/info/gcc-8*
              • sudo apt-get install --reinstall gcc-8
          • pip3 --version
        • Install git (required by freqtrade) (~ 3 minutes + 16 minutes)
          • sudo apt install git-all
          • git --version
        • Install virtualenv(recommended by freqtrade) (~2 minutes)
          • sudo apt-get install python3-venv
          • sudo apt install python3-virtualenv
          • virtualenv --help
        • Install LATEST version of FREQTRADE (~40 minutes)
          • git clone https://github.com/freqtrade/freqtrade.git
          • cd freqtrade
          • ./setup.sh --install
            • reset git branch? yes
            • install dependecies for dev? no
            • install plotting dependencies? yes
            • install hyperopt dependencies? yes
        • VM1 Desktop > Ubuntu Software: (if error arises when installing any app below, see solution here: https://askubuntu.com/questions/1246668/ubuntu-software-error-error-opening-directory-usr-share-appdata-no-such-fil)
          • install "Telegram Desktop"
          • install "Atom" (snappyatom) > Packages > Tree View > Focus > Rightclick left panel > Add Project Folder > home\user1\freqtrade\
      • Changes in location of main files:
        • config.json : no change (/home/user1/freqtrade/config.json)
        • bbrsi.py : 
          • From: /home/user1/freqtrade/freqtrade/strategy/bbrsi.py
          • To:      /home/user1/freqtrade/user_data/strategies/bbrsi.py
        • bbrsi_opt:
          • From: /home/user1/freqtrade/freqtrade/optimize/bbrsi_opt.py
          • To:     /home/user1/freqtrade/user_data/hyperopts/bbrsi_optx.py

      Some commands for latest version of freqtrade (/freqtrade/docs)
      • Verify freqtrade successfully installed:
        • user1@user1-computer1:~/freqtrade$ source .env/bin/activate
        • freqtrade --version
      • Get help
        • freqtrade --help
      • Generate new config file
        • freqtrade new-config -c config.json
      • Run the bot
        • source .env/bin/activate
        • freqtrade <subcommand>
      • Download data 
        • 1st time: for the intervals (-t) "15m" and "1h", for the past 90 days (--days 90), for the exchange and pairs as specified (-c) in config.json, (and before starting the d/load, to erase all existing data for the earlier-mentioned exchange/pairs/timeframes (--erase))
          • user1@computer1:~/freqtrade$ freqtrade download-data -t {15m,1h} --days 90 -c config_001.json --erase
        • Subsequent times: If need to fetch past 10 days data (don't do it if <10 days, entire dataset might be erased), then fetch slightly over 10 days e.g. 15 days:
          • user1@computer1:~/freqtrade$ freqtrade download-data -t {15m,1h} --days 15 -c config.json
      • Update config.json:
        • pair_whitelist
        • "exchange": {
        •         "name": "binance",
        •         "key": "your_exchange_key",
        •         "secret": "your_exchange_secret",
        • "telegram": {
        •         "enabled": true,
        •         "token": "your_telegram_token",
        •         "chat_id": "your_telegram_chat_id"
      • Strategy
        • Create new strategy from scratch:
          • (.env) user1@user1-computer1:~/freqtrade$ freqtrade new-strategy --strategy BBRSI_001
          • New strategy file saved in: user_data/strategies/BBRSI_001.py
      • Backtest
        • Run backtest, using custom strategy class "BBRSI_001" in file freqtrade/user_data/strategies/bbrsi.py (-s BBRSI_001), on specific pairs at a specific exchange as described in a specific config file (--config config_001.json), using time interval 15m (--timeframe 15m), across a specific period (--timerange=20201015-20201220), then save backtest results for plotting (--export trades), to a specific file (--export-filename=backtest_bbrsi.json)
          • (.env) user1@user1-computer1:~/freqtrade$ freqtrade backtesting --config config_001.json -s BBRSI_001 --timeframe 15m --timerange=20191222-20200821 --export trades
            • Errors?
              • TypeError: 'Series' object is not callable
                • Add braces: from "dataframe['close'] > 1.8335 * dataframe['ema9']" to "(dataframe['close'] > 1.8335 * dataframe['ema9'])"
          • When performing backtest to verify the hyperopt results: If backtest results are very different from hyperopt results:
            • try to run backtest together with "--eps" and/or "--dmmp". Ideally, don't use "--eps" & "--dmmp" bcos sandbox and live modes do not use it. eps means buy the same pair multiple times. dmmp means bot can have infinite open trades.
            • check they are correct: timeframe, timerange
            • check that the sell settings in strategy file are identical to "ask_strategy" in config file:
              • use_sell_signal = True
              • sell_profit_only = False
              • ignore_roi_if_buy_signal = False
            • check in the "buy" and "sell" sections of the strategy file: are there any statements (e.g. guards or triggers) that are irrelevant / should be commented out?
          • Backtest results saved to: /freqtrade/user_data/backtest_results/backtest_results-yyyy-mm-dd_hh-mm-ss.json
      • Optimization
        • Create new hyperopt from scratch:
          • (.env) user1@user1-computer1:~/freqtrade$ freqtrade new-hyperopt --hyperopt BBRSI_OPT_001
          • New hyperopt file saved in : user_data/hyperopts/BBRSI_OPT_001.py
        • Run hyperopt, with specific config file (--config config_001.json), using custom hyperopt file (--hyperopt BBRSI_OPT_001), and getting the 3 functions "populate_indicators" "populate_buy_trend" & "populate_sell_trend" from strategy BBRSI_001 (--strategy BBRSI),  using the loss function "SharpeHyperOptLossDaily" as found in "user_data/hyperopts/", and do 1000 iterations (-e 1000), and optimize all possible parameters (--spaces all), applied to data within specific period (--timerange 20191222-20200821)
          • (.env) user1@user1-computer1:~/freqtrade$ freqtrade hyperopt --config config_001.json --hyperopt BBRSI_OPT_001 --strategy BBRSI_001 --hyperopt-loss SharpeHyperOptLossDaily -e 1000 --spaces all --timerange 20191222-20200821
          • Hyperopt results saved to: /freqtrade/user_data/hyperopt_results/hyperopt_results-yyyy-mm-dd_hh-mm-ss.pickle
          • FOR BEST RESULTS: Run this command 10x. Bcos 1000 iterations x 10 different starting random states produce the best results.
          • Low virtual disk space? https://www.youtube.com/watch?v=Fteij2amMos
          • Errors?
            • "freqtrade - ERROR - No pair in whitelist." ==> API down. See Binance FB. Or https://www.binance.com/en/support/search?type=1&q=upgrade%20maintenance
              • Important: 
              • dietpi@DietPi:~$  sudo reboot now (MUST manually do this after the exchange maintenance period is over, in order to get the bot to resume trading)
            • Already downloaded data but still get error "WARNING - No history data for pair: "xxx/BTC", timeframe: 15m...." ? ==> check your timerange
            • "ValueError: Expected object or value" ==> redownload the data
          • Blackscreen?
            • Likely due to heavy operation and limited memory
            • VM1 menu: Machine > Session Information > "Performance Monitor" tab > "RAM Usage" could have hit max limit
            • Wait for "RAM Usage" to drop below max limit and the blackscreen should be replaced by the usual lockscreen (then enter pwd to unlock)
            • Consider increasing the machine's base memory limit . How? VirtualBox > select machine (VM1) > Settings > System > "Motherboard" tab >  drag to increase "Base Memory")
            • IMPORTANT: If blackscreen doesn't go away and u reboot VM and VM has vguest error ([example of error message here]), then: (1) don't create a new VM yet (this is a time-consuming last resort), but try this first: https://itsfoss.com/virtualbox-guest-additions-ubuntu/ (i.e. (1) insert guest addition cd image and run software (2) see the error "" (3) run "sudo apt install build-essential dkms linux-headers-generic" (4) run "sudo rcvboxadd setup" (5) click top right "on/off symbol" button > PowerOff/LogOut > Power Off > Restart (may have to do this step (5) twice until the VGuest Addition works again)
        • List the best hyperopt iteration
          • After hyperopting several times, u will generate many hyperopt result files
          • Use this command to look into each file, and pick the best iteration (highest SR) from among all the files
            • See a list of iterations (hyperopt-list), showing only the best i.e. SR keeps improving (--best), and profitable iterations (--profitable), in a specific hyperopt results file (--hyperopt-filename hyperopt_results_2020-12-20_21-35-02.pickle)
              • freqtrade hyperopt-list --best --profitable --hyperopt-filename=hyperopt_results_2020-12-20_21-35-02.pickle 
          • Show a specific hyperopt iteration's details
            • Show a hyperopt's details (hyperopt-show), of a specific hyperopt results file (--hyperopt-filename hyperopt_results_2020-12-20_21-35-02.pickle), for a particular iteration (--index 550)
              • freqtrade hyperopt-show --hyperopt-filename=hyperopt_results_2021-01-21_00-02-39.pickle --index 550 
          • View visually
            • Create plot (plot-dataframe), using specific config file (--config config_001.json), that shows specific pairs (-p AION/BTC ADA/BTC), for a custom strategy (--strategy BBRSI), using one set of indicators which use price units (--indicators1 bb_lowerband1 bb_lowerband2), using another set of indicators which don't use price units (--indicators2 rsi, fisher_rsi), for a specific period (--timerange=20201015-20201220), using data from a backtest result file (--export-filename user_data/backtest_results/backtest-result-2020-12-21_09-38-07.json),
              • (.env) user1@computer1:~/freqtrade$ freqtrade plot-dataframe --config config_001.json -p AION/BTC ADA/BTC --strategy BBRSI_001 --indicators1 bb_lowerband1 bb_lowerband2 --indicators2 rsi fisher_rsi --timerange=20201015-20201220 --export-filename user_data/backtest_results/backtest-result-2020-12-21_09-38-07.json
            • Note: each pair generates 1 file. The last 2 lines will show location of the file e.g. /home/user1/freqtrade/user_data/plot/freqtrade-plot-AION_BTC-15m.html
            • See plot
              • (.env) user1@computer1:~/freqtrade$ firefox /home/user1/freqtrade/user_data/plot/freqtrade-plot-AION_BTC-15m.html
              • Navigation: 
                • To zoom, drag an intentional and complete box, defining the topleft & bottomright corners. Can be done on any area (main plot, volume plot, other plot). Doubleclick to zoom out.
                • To view any indicator in the "indicators1" group: they will be on same chart as the pair's candlesticks.
                • To view any indicator in the "indicators2" group: Since every indicator could have different scales e.g. 0 to 100, -1.0 to +1.0, etc... some may appear shrunken to the point of being hidden. To see the "shrunken/hidden" indicator, need to hide the visible indicators i.e. click on the visible indicator in the chart's legend. Example: To see fisher_rsi (range is -1.0 to +1.0), need to hide rsi (range is 0 to 100), so, click on "rsi" in the legend to hide "rsi", and then "fisher_rsi" will expand to be more visible.
              • Note: If the pair exits due to minimal ROI (and not due to a sell signal), the plot won't show a sell signal.
          • Setup Telegram bot
          • Sandbox mode
            • Setup
              • config.json: "dry_run": true,
            • Run bot, with specific strategy (-s BBRSI), using specific config file (-c config.json), and read/write into specific database (--db-url sqlite:///user_data/trades004.dryrun.sqlite)
              • (.env) user1@computer1:~/freqtrade$ freqtrade trade -s BBRSI -c config.json --db-url sqlite:///user_data/trades001.dryrun.sqlite
                • Not getting msgs from telegram? Check if telegram is enabled in config.
          • Live mode
            • Setup:
              • config.json: "dry_run": false,
            • Run bot, with specific strategy (-s BBRSI), using specific config file (-c config.json), and read/write into specific database (--db-url sqlite:///user_data/trades004.live.sqlite)
              • (.env) user1@computer1:~/freqtrade$ freqtrade trade -s BBRSI -c config.json --db-url sqlite:///user_data/trades004.live.sqlite 
            • No trades happening?
              • Strategy may currently be in a quiet period: If backtest said to expect 40 trades/day, then allow maybe 3 days to see a trade appear. Distribution of trades is not even across all days.
              • Strategy mistakenly use future data (backtest won't detect this error). Ensure strategy doesn't use anything that references future data, example:
                • shift(-1)
              • Strategy mistakenly used absolute position in dataframe. Example: 
                • .iloc[-1]
              • Funds must be in binance.com's SPOT wallet (not other wallets e.g. P2P wallet)
              • API keys must be correct or updated

          • How to decide which pairs to trade?
            • Specific pairs: config.json:
              • "pairlists": [
              •         {"method": "StaticPairList"}
              •     ],
              • "pair_whitelist": [
              •           "ADA/BTC",
              •           "ZIL/BTC"
              •         ],
              • "pair_blacklist": [
              •             "DOGE/BTC",
              •             "BNB/BTC"
              •         ]
            • Top pairs by volume: config.json:
              • Trade pairs which are filtered  by volume (method: VolumePairList), sorted by volume (quoteVolume), take the top 20 pairs ("number_assets": 20,), and refresh the list of pairs every 1800 seconds.
              • "pairlists": [{
              •         "method": "VolumePairList",
              •         "number_assets": 20,
              •         "sort_key": "quoteVolume",
              •         "refresh_period": 1800
              • }],
          • How to close all positions gracefully on Telegram
            • /stopbuy (stops buying, continues trying to sell existing open positions)
              • Note: use "/reload_config" (to undo the "/stopbuy" command)
            • /status table (see remaining open positions)
            • /stop (stops freqtrade. Do this once all open positions are closed)
              • Note: use "/start" (to undo the "/stop" command)
          CONTINUE AT 6:28 @ CHAPTER 53 (Edge Positioning)

          Popular Posts