One of the ongoing research projects inside the Robot Wealth community involves an FX strategy with some multi-week hold periods. Such a strategy can be significantly impacted by the swap, or the cost of financing the position. These costs change over time, and we decided that for the sake of more accurate simulations, we would incorporate these changes into our backtests.
This post shows you how to simulate variable FX swaps in both Python and the Zorro trading automation software platform.
What is Swap?
The swap (also called the roll) is the cost of financing an FX position. It is typically derived from the central bank interest rate differential of the two currencies in the exchange rate being traded, plus some additional fee for your broker. Most brokers apply it on a daily basis, and typically apply three times the regular amount on a Wednesday to account for the weekend. Swap can be both credited to and debited from a trader’s account, depending on the actual position taken.
Why is it Important?
Swap can have a big impact on strategies with long hold periods, such as the typical momentum strategy. Therefore, accurately accounting for it is important in such cases. Zorro’s default swap calculation relies on a constant derived from the Assets List used in the simulation, which is fine for most situations, but might lead to unrealistic results when the hold period is very long.
Simulating Variable Swaps in Zorro and in Python
Here’s some code for simulating historical swaps. It takes historical central bank data from the Bank of International Settlements, via Quandl. I’ve included code for the historical interest rates of the G8 countries – to get others, you just need the relevant Quandl code.
For the Zorro version, you’ll also need Zorro S, as the Quandl bridge is not available in the free version of Zorro. However, at the end of this article, I’ve also included a Python script for downloading the data from Quandl that you can save and then import into your backtesting platform. The advantage of the Zorro version is that you can access the relevant data from within a trading script via direct link to the Quandl API. That’s super convenient and all but eliminates the need to do any data wrangling at all. The advantage of the Python version is that it is completely free, but using the data in a trading script requires a little more messing around.
The Zorro Version
In order to access data from Quandl within Zorro, you’ll need a Quandl API key (get it from the Quandl website) and enter it in your ZorroFix.ini or Zorro.ini file.
Here’s the Zorro script:
/* Download historical central bank policy rates from Quandl and use to calculate historical swaps. Zorro's FX swap is interest per day per 10000 units traded, in account currency. */ #include <contract.c> var calculate_roll_long(var base_ir, var quote_ir, var broker_fee) { /*Calculates Zorro roll long in units of quote currency*/ var ird = (base_ir - quote_ir)/100; return 10000*ird/365 - broker_fee; } var calculate_roll_short(var base_ir, var quote_ir, var broker_fee) { /*Calculates Zorro roll short in units of quote currency*/ var ird = (quote_ir - base_ir)/100; return 10000*ird/365 - broker_fee; } function run() { set(PLOTNOW); PlotWidth = 800; PlotHeight1 = 400; PlotHeight2 = 250; StartDate = 20100101; EndDate = 20180630; // daily policy rates of major central banks, from Bank of International Settlements, via Quandl var usd_ir = dataFromQuandl(1, "%Y-%m-%d,f", "BIS/PD_DUS", 1); var jpy_ir = dataFromQuandl(2, "%Y-%m-%d,f", "BIS/PD_DJP", 1); var aud_ir = dataFromQuandl(3, "%Y-%m-%d,f", "BIS/PD_DAU", 1); var eur_ir = dataFromQuandl(4, "%Y-%m-%d,f", "BIS/PD_DXM", 1); var cad_ir = dataFromQuandl(5, "%Y-%m-%d,f", "BIS/PD_DCA", 1); var chf_ir = dataFromQuandl(6, "%Y-%m-%d,f", "BIS/PD_DCH", 1); var nzd_ir = dataFromQuandl(7, "%Y-%m-%d,f", "BIS/PD_DNZ", 1); var gbp_ir = dataFromQuandl(8, "%Y-%m-%d,f", "BIS/PD_DGB", 1); // What the broker takes in addition to the interest rate differential // Will vary by broker, by pair, and even by direction! Make a conservative assumption. var broker_fee = 0.5; // EUR/USD roll in AUD example asset("EUR/USD"); //calculate roll long in units of quote currency var rl = calculate_roll_long(eur_ir, usd_ir, broker_fee); // convert to units of account currency - here the account currency is AUD // not required if account currency is the same as the quote currency string current_asset = Asset; // store name of currently selected asset asset("AUD/USD"); // switch to ACCT_CCY/QUOTE_CCY var p = priceClose(); asset(current_asset); // switch back to original asset RollLong = rl/p; // adjust roll long calculation and set Zorro's RollLong variable //calculate roll short in units of quote currency var rs = calculate_roll_short(eur_ir, usd_ir, broker_fee); // convert to units of account currency - here the account currency is AUD // not required if account currency is the same as the quote currency RollShort = rs/p; // adjust roll short calculation and set Zorro's RollShort variable // plot roll in units of account currency plot("Roll Long", RollLong, NEW, BLUE); plot("Roll Short", RollShort, 0, RED); }
One major thing to remember is that your FX broker won’t charge/pay swaps based on the exact interest rate differential. In practice, they might take some additional fat for themselves, or even adjust their actual swaps on the basis of perceived upside/downside volatility – and these may not even be symmetrical! The short story is that the broker’s cut will vary by broker, FX pair, and even by direction! You can verify that yourself by searching various brokers’ websites for their current swap rates.
So the upshot of all that is that if you want to include an additional broker fee in your simulation, recognise that it will be an estimate, do some research on what brokers are currently charging, and err on the conservative side. In the code above, the broker fee is set in line 44; you can also set this to zero if you like.
The trickiest part is converting the interest rate differential of the base-quote currencies to Zorro’s RollLong and RollShort variables – but the advantage is that once you get that right, Zorro will take care of simulating the roll for you – you literally won’t have to do another thing! These variables represent the swap in account currency per 10,000 traded FX units. Most of that conversion is taken care of the in the calculate_roll_long() and calculate_roll_short() functions in the code above. But these functions output the swap in units of the quote currency, not the account currency. This requires some more conversion.
The code also contains an example of converting the EUR/USD roll for an account denominated in AUD. This is accomplished from line 46.
Here’s the output of running the script. You can see how the swap for long and short trades has changed over time. At some point in 2014, it became a less expensive proposition to sell the EUR against the USD rather than buy it. You can also see that the value of the swap is constantly changing; that’s because the calculation considers the contemporaneous exchange rate of the account currency (AUD) against the quote currency (USD) of the pair being traded.
The Python Version
Here’s a python script for downloading the same data set as used above (albeit with a longer history) from Quandl, and a function for calculating the swap. This time, the function calculates the swap per standard FX lot, which is 100,000 units of the quote currency (the Zorro script above calculates the swap per 10,000 units which is required for Zorro’s RollLong and RollShort variables).
import pandas as pd import matplotlib.pyplot as plt import quandl cad = quandl.get("BIS/PD_DCA") jpy = quandl.get("BIS/PD_DJP") chf = quandl.get("BIS/PD_DCH") aud = quandl.get("BIS/PD_DAU") gbp = quandl.get("BIS/PD_DGB") nzd = quandl.get("BIS/PD_DNZ") eur = quandl.get("BIS/PD_DXM") usd = quandl.get("BIS/PD_DUS") # this is the effective fed funds rate def calculate_rolls(base, quote, broker_fee): ird = 100000*(base - quote)/(100*365) - broker_fee ird.columns = ["IRD"] ird.fillna(method="ffill", inplace=True) ird["roll_long"] = ird["IRD"] - broker_fee ird["roll_short"] = -ird["IRD"] - broker_fee return ird
Plotting the historical effective fed funds rate, you can see that the data set might have some problems prior to about 1985. You may need to smooth the data or remove outliers to use it effectively.
ax = usd.plot(grid=True) ax.legend(["USD Effective Fed Funds Rate"])
We can simulate and plot the historical swap of the AUD/CAD exchange rate as follows:
broker_fee = 5 # how much does the broker take per lot of the quote currency? aud_cad = calculate_rolls(aud, cad, broker_fee) aud_cad[["roll_long", "roll_short"]].dropna().plot(grid=True)
Again, you can see some potential data issues prior to about 1990.
Conclusion
The cost of financing a long-term FX position can have a significant impact on the overall result of the trade. This post demonstrated a simple and inexpensive way to simulate the historical variable financing costs for FX.
1 thought on “Simulating Variable FX Swaps in Zorro and Python”