forked from gbeced/basana
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbacktest_pairs_trading.py
More file actions
108 lines (89 loc) · 4.26 KB
/
Copy pathbacktest_pairs_trading.py
File metadata and controls
108 lines (89 loc) · 4.26 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# Basana
#
# Copyright 2022 Gabriel Martin Becedillas Ruiz
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Bars can be downloaded using these commands:
# python -m basana.external.binance.tools.download_bars -c BCH/USDT -p 1h -s 2021-12-01 -e 2021-12-26 \
# -o binance_bchusdt_hourly.csv
# python -m basana.external.binance.tools.download_bars -c CVC/USDT -p 1h -s 2021-12-01 -e 2021-12-26 \
# -o binance_cvcusdt_hourly.csv
from decimal import Decimal
import asyncio
import datetime
import logging
from basana.backtesting import charts, lending
from basana.core.logs import StructuredMessage
from basana.external.binance import csv
import basana as bs
import basana.backtesting.exchange as backtesting_exchange
from samples.backtesting import position_manager
from samples.strategies import pairs_trading
async def main():
logging.basicConfig(level=logging.INFO, format="[%(asctime)s %(levelname)s] %(message)s")
event_dispatcher = bs.backtesting_dispatcher()
quote_symbol = "USDT"
pair_1 = bs.Pair("BCH", quote_symbol)
pair_2 = bs.Pair("CVC", quote_symbol)
# We'll be opening short positions so we need to set a lending strategy when initializing the exchange.
lending_strategy = lending.MarginLoans(quote_symbol, default_conditions=lending.MarginLoanConditions(
interest_symbol=quote_symbol, interest_percentage=Decimal("10"),
interest_period=datetime.timedelta(days=365), min_interest=Decimal("0.01"),
margin_requirement=Decimal("0.5")
))
exchange = backtesting_exchange.Exchange(
event_dispatcher,
initial_balances={quote_symbol: Decimal(1200)},
lending_strategy=lending_strategy,
)
exchange.set_symbol_precision(pair_1.base_symbol, 8)
exchange.set_symbol_precision(pair_2.base_symbol, 8)
exchange.set_symbol_precision(quote_symbol, 2)
# When opening positions, either long or short, size each order to 1000 USDT and close the position if the loss
# is greater than, or equal to, 5%.
position_mgr = position_manager.PositionManager(
exchange, position_amount=Decimal(1000), quote_symbol=quote_symbol, stop_loss_pct=Decimal(5)
)
# The pairs trading strategy.
p_value_threshold = 0.01
trading_strategy = pairs_trading.Strategy(
event_dispatcher, pair_1, pair_2, window_size=24 * 10, z_score_window_size=24 * 10,
p_value_threshold=p_value_threshold, z_score_entry_ge=2.3, z_score_exit_lt=1.5
)
# Connect the position manager to the strategy signals.
trading_strategy.subscribe_to_trading_signals(position_mgr.on_trading_signal)
chart = charts.LineCharts(exchange)
for pair in [pair_1, pair_2]:
# Load bars from the CSV file.
bars_file_name = "binance_{}{}_hourly.csv".format(pair.base_symbol.lower(), pair.quote_symbol.lower())
exchange.add_bar_source(csv.BarSource(pair, bars_file_name, "1h"))
# Connect the strategy to the bar events from the exchange.
exchange.subscribe_to_bar_events(pair, trading_strategy.on_bar_event)
# Connect the position manager to the bar events from the exchange.
exchange.subscribe_to_bar_events(pair, position_mgr.on_bar_event)
chart.add_pair(pair)
chart.add_custom("P-Value", "P-Value", lambda _: trading_strategy.p_value)
chart.add_custom("P-Value", "Threshold", lambda _: p_value_threshold)
chart.add_custom("Z-Score", "Z-Score", lambda _: trading_strategy.z_score)
chart.add_portfolio_value(quote_symbol)
# Run the backtest.
await event_dispatcher.run()
# Log balances.
balances = await exchange.get_balances()
for currency, balance in balances.items():
logging.info(StructuredMessage(
f"{currency} balance", available=balance.available
))
chart.show()
if __name__ == "__main__":
asyncio.run(main())