Tag Forex

Posts: 41

Python Forex Trading Bot

I have had some time to continue on my Python Forex Trading Bot now that we're all self isolating. This is purely for educational purposes because when I run this sucker, it loses money. Not so much anymore but it's not profitable. The reason why I say 'educational purposes' is that coding is not my first choice of career and I teach myself as I go along. Coding's been very profitable in other parts of my life and I use it to get $hit done.

I now understand the concept of Classes, which is great because it makes pieces of code very 'pluggable.' Originally I thought I could write a set of functions in the MomentumTrader class that would serve as my Stop and Trailing Stop orders. I did do that only to find out that I was creating those orders AFTER the trade and when the Bot would try to close out or add to the position (as it does because it's a mean reversion strategy) it would sometimes crash. This led me to find a set classes in the API called onFill. This eliminated the need for me to create the order first and THEN add in a stop or trailing stop. I was able to do it once the trade was filled. The moral of the story, you should really understand your API classes.

Overall the API extended by Feite is quite robust and powerful, but it's still very hard to make any money with this thing. Although I've been whining about getting active again, the reality is that the long term wins.

I'll continue to test this over the course of the next few weeks using an Oanda Practice Account but I think I'm going to write a new class that best mimics my current Forex trading style instead. I use the Daily chart, trade pairs where I make $ from the carry trade, and do a long term trend play. That's the beauty of Forex, you can see some great long trends if you zoom out.

My discretionary trading system does have some flaws. I usually get the entry wrong and have to place a second trade to 'scale in.' It's something I don't like doing because it means more risk. I also need to work on proper risk management as well. Right now I don't use stops and I routinely take on 200 pip swings. This has worked out for me because 99% of the time I trade the EURUSD pair, which has been in a long downward trend. I usually make a short entry, then the price turns against me and goes higher, then I place another short entry where the price stabilizes. I think I've been very lucky until now and my trading metrics and expectancy are positive. Still, I feel like I leave a lot to chance and I'd like to size my position accordingly, make better entries, and use better risk management.

Current Python Forex Trading Bot

So here's the latest incarnation of the Bot. I spent some time clean it up and adding in a trailingstop onfill function.

#Install Py Package from: https://github.com/hootnot/oanda-api-v20
#https://oanda-api-v20.readthedocs.io/en/latest/oanda-api-v20.html

import json
import oandapyV20 as opy
import oandapyV20.endpoints.instruments as instruments
from oandapyV20.contrib.factories import InstrumentsCandlesFactory

import pandas as pd
from pandas.io.json import json_normalize

from oandapyV20.exceptions import V20Error, StreamTerminated
from oandapyV20.endpoints.transactions import TransactionsStream
from oandapyV20.endpoints.pricing import PricingStream
from oandapyV20.contrib.requests import TrailingStopLossOrderRequest

import datetime
from dateutil import parser

import numpy as np
def exampleAuth():
    accountID, token = None, None
    with open("./oanda_account/account.txt") as I:
        accountID = I.read().strip()
    with open("./oanda_account/token.txt") as I:
        token = I.read().strip()
    return accountID, token
instrument = "EUR_USD"

#Set time functions to offset chart
today = datetime.datetime.today()
two_years_ago = today - datetime.timedelta(days=720)

current_time = datetime.datetime.now()

twentyfour_hours_ago = current_time - datetime.timedelta(hours=12)
print (current_time)
print (twentyfour_hours_ago)
#Create time parameter for Oanada call
ct = current_time.strftime("%Y-%m-%dT%H:%M:%SZ")
tf = twentyfour_hours_ago.strftime("%Y-%m-%dT%H:%M:%SZ")
#Connect to tokens
accountID, access_token = exampleAuth()
client = opy.API(access_token=access_token)
params={"from": tf,
        "to": ct,
        "granularity":'M1',
        "price":'A'}
r = instruments.InstrumentsCandles(instrument=instrument,params=params)
#Do not use client from above
data = client.request(r)
results= [{"time":x['time'],"closeAsk":float(x['ask']['c'])} for x in data['candles']]
df = pd.DataFrame(results).set_index('time')

df.index = pd.DatetimeIndex(df.index)
from oandapyV20.endpoints.pricing import PricingStream
import oandapyV20.endpoints.orders as orders
from oandapyV20.contrib.requests import MarketOrderRequest, TrailingStopLossDetails, TakeProfitDetails
from oandapyV20.exceptions import V20Error, StreamTerminated
import oandapyV20.endpoints.trades as trades

class MomentumTrader(PricingStream): 
    def __init__(self, momentum, *args, **kwargs): 
        PricingStream.__init__(self, *args, **kwargs)
        self.ticks = 0 
        self.position = 0
        self.df = pd.DataFrame()
        self.momentum = momentum
        self.units = 1000
        self.connected = False
        self.client = opy.API(access_token=access_token)

    def create_order(self, units):
        #You can write a custom distance value here, so distance = some calculation

        trailingStopLossOnFill = TrailingStopLossDetails(distance=0.0005)

        order = orders.OrderCreate(accountID=accountID, 
                                   data=MarketOrderRequest(instrument=instrument, 
                                                           units=units, 
                                                           trailingStopLossOnFill=trailingStopLossOnFill.data).data)
        response = self.client.request(order)
        print('\t', response)

    def on_success(self, data):
        self.ticks += 1
        print("ticks=",self.ticks)
        # print(self.ticks, end=', ')

        # appends the new tick data to the DataFrame object
        self.df = self.df.append(pd.DataFrame([{'time': data['time'],'closeoutAsk':data['closeoutAsk']}],
                                 index=[data["time"]]))

        #transforms the time information to a DatetimeIndex object
        self.df.index = pd.DatetimeIndex(self.df["time"])

        # Convert items back to numeric (Why, OANDA, why are you returning strings?)
        self.df['closeoutAsk'] = pd.to_numeric(self.df["closeoutAsk"],errors='ignore')

        # resamples the data set to a new, homogeneous interval, set this from '5s' to '1m'
        dfr = self.df.resample('60s').last().bfill()

        # calculates the log returns
        dfr['returns'] = np.log(dfr['closeoutAsk'] / dfr['closeoutAsk'].shift(1))

        # derives the positioning according to the momentum strategy
        dfr['position'] = np.sign(dfr['returns'].rolling(self.momentum).mean())

        print("position=",dfr['position'].iloc[-1])

        if dfr['position'].iloc[-1] == 1:
            print("go long")
            if self.position == 0:
                self.create_order(self.units)

            elif self.position == -1:
                self.create_order(self.units * 2)
            self.position = 1

        elif dfr['position'].iloc[-1] == -1:
            print("go short")
            if self.position == 0:
                self.create_order(-self.units)


            elif self.position == 1:
                self.create_order(-self.units * 2)

            self.position = -1

        if self.ticks == 25000:
            print("close out the position")
            if self.position == 1:
                self.create_order(-self.units)
            elif self.position == -1:
                self.create_order(self.units)
            self.disconnect()

    def disconnect(self):
        self.connected=False

    def rates(self, account_id, instruments, **params):
        self.connected = True
        params = params or {}
        ignore_heartbeat = None
        if "ignore_heartbeat" in params:
            ignore_heartbeat = params['ignore_heartbeat']
        while self.connected:
            response = self.client.request(self)
            for tick in response:
                if not self.connected:
                    break
                if not (ignore_heartbeat and tick["type"]=="HEARTBEAT"):
                    print(tick)
                    self.on_success(tick)
# Set momentum to be the number of previous 5 second intervals to calculate against

mt = MomentumTrader(momentum=60,accountID=accountID,params={'instruments': instrument})
print (mt)
mt.rates(account_id=accountID, instruments=instrument, ignore_heartbeat=True)

comments

Updated Forex Charts with Bokeh

Just updating my past post on autogenerating support and resistance lines for the EURUSD. I've updated the charts to use Bokeh, a nicer and interactive set of plots. You can zoom in and see the candles at various price levels. I still need to do more labeling work on the charts as I learn how to use the Bokeh library. The thing that's hanging me up right now is how to correctly label the horizontal support and resistance lines (aka as Spans in Bokeh) with the actual value. The other thing I need to do is add a legend. Red means resistance and Green means support.

The charts work on 2 years of data with a 20 day look back period for generating the support and resistance lines. In the future I want to optimize the look back period using some other method. What method? I don't know but that's on the back burner until I figure out how to generate a single webpage with the charts loaded into them.

In case you want to see what gets autogenerated every Saturday morning, you can click on the EUR_USD, USD_JPY, and GBP_USD currency pairs. If there any other ones you want to see, drop me a comment below and I'll add them.

UPDATE

I added the expected returns for a momentum startegy to each currency pair chart. The idea is to see if you can be profitable with momentum trades over 15, 30, 60, and 120 days. Some results are very interesting. I have a Momentum Trading Bot all built and ready to go. I just need to set up my Oanda account correctly and then I'll start 'practice' trade it.

I also added some stockcharts for MSFT, GOOG, AMZN, DATA, AAPL and NVDA. These get updated daily and they're for a 15 day time period. Same as above, green color is support and red is resistance.

Some Crypto Currency support for BTC and XRP. Still a work in progress!

comments

Weekly Support and Resistance Updates to EURUSD

Over the years I've used labs.neuralmarkettrends.com  as my development sandbox. Sometimes it was to test out new CMS's (like Expression Engine) and other times it's cron jobbing scripts. For the most part, it's cron jobbing scripts. I started thinking about using  that site as a Django dev site a few months ago. I'd like to learn how to build a website from scratch using Python and Django and thought this would be a good place as any. 

The goal is to build a simple dashboard where a user can enter a  currency pair and get some results. My current script only generates support and resistance lines for the EURUSD pair. It updates once a week on Saturdays. Mind you, the formatting of the graphic is terrible and how I link to it is worse. Like I said, this is a sandbox.

I want to transition the graphic to a better open source visualization library. One idea is to investigate using Bokeh which has nice interactive visualizations. I don't expect this project to move at lightspeed, it'll move at a glacial pace over the next few months or years. 

If you want to see the weekly updates of the EURUSD pair, go here.

comments

Autogenerating Support and Resistance Lines for Forex

On the heels of my last post, I've extended those functions to the EURUSD pair. The data starts from this year 2019 and goes through to yesterday. It's actually a pretty neat script as it takes data from Onada and then generates the support and resistance lines for that particular pair. The next step would be to create a buy/sell order in the Oanda Practice Account. Once I do that it's then a matter of writing a trading strategy and testing it in real time.

Everything I'm doing is completely academic and modular right now. I have no idea how really build a Forex Trading Bot or even what strategies to use here. It's more of a "can I do it" endeavor.

Autogenerated Support and Resistance Lines, EURUSD

I'm fully convinced that retail traders that can learn Python can automate their entire trading strategies. Now, the flipside here is if their strategies are worth anything. Just because we can automated trading setups, we must always ask "why do I think I'm right?"

comments

Autogenerating Support and Resistance Lines

Work has been keeping me busy but I found sometime to figure out how to autogenerate support and resistance lines. I cobbled together some code I found online and then made a simple plot. I'm doing this to help me identify the 'zones' in Forex (mostly) and see if I can automate a trading bot to make trades for me.

Here's a chart from two years worth of daily S&P500 closes. On the surface, the lines look pretty decent but the real trick is figuring out what the right lookback period should be. Here my lookback period was 20 days.

There's more work to be done, I have to fix the x-axis to show the dates and get a larger time period. I'm even testing automating this script into some dashboard. Right now you can see a crappy jpg of the EURUSD currency pair on my labs site.

The generic code to build these support and resistance lines is here.

comments

Neural Market Trends is the online home of Thomas Ott.