Areix IO Backtesting Framework
Project description
Areix IO (Alpha Test)
Installation
Create a virtual environment
virtualenv venv --python=python3
Activate the virtual environment
# Macbook / Linus
source venv/bin/activate
# Windows
venv/Scripts/activate
Deactivate
deactivate
Install Areix-IO package
pip install Areix-IO
Usage
Define your strategy class:
from areix_io import (
create_report_folder, SideType, set_token,
Strategy, CryptoDataFeed, BackTest, BackTestBroker
)
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, f1_score
import pandas as pd
import numpy as np
PRED_DAYS = 2
PCT_CHANGE = 0.004
'''
Data pre processing step
'''
def bollinger_band(data, n_lookback, n_std):
hlc3 = (data['high'] + data['low'] + data['close']) / 3
mean, std = hlc3.rolling(n_lookback).mean(), hlc3.rolling(n_lookback).std()
upper = mean + n_std*std
lower = mean - n_std*std
return upper, lower
def update_df(df):
upper, lower = bollinger_band(df, 20, 2)
df['ma10'] = df.close.rolling(10).mean()
df['ma20'] = df.close.rolling(20).mean()
df['ma50'] = df.close.rolling(50).mean()
df['ma100'] = df.close.rolling(100).mean()
df['x_ma10'] = (df.close - df.ma10) / df.close
df['x_ma20'] = (df.close - df.ma20) / df.close
df['x_ma50'] = (df.close - df.ma50) / df.close
df['x_ma100'] = (df.close - df.ma100) / df.close
df['x_delta_10'] = (df.ma10 - df.ma20) / df.close
df['x_delta_20'] = (df.ma20 - df.ma50) / df.close
df['x_delta_50'] = (df.ma50 - df.ma100) / df.close
df['x_mom'] = df.close.pct_change(periods=2)
df['x_bb_upper'] = (upper - df.close) / df.close
df['x_bb_lower'] = (lower - df.close) / df.close
df['x_bb_width'] = (upper - lower) / df.close
# df = df.dropna().astype(float)
return df
def get_X(data):
return data.filter(like='x').values
def get_y(data):
y = data.close.pct_change(PRED_DAYS).shift(-PRED_DAYS) # Returns after roughly two days
y[y.between(-PCT_CHANGE, PCT_CHANGE)] = 0 # Devalue returns smaller than 0.4%
y[y > 0] = 1
y[y < 0] = -1
return y
def get_clean_Xy(df):
X = get_X(df)
y = get_y(df).values
isnan = np.isnan(y)
X = X[~isnan]
y = y[~isnan]
return X, y
class MLStrategy(Strategy):
num_pre_train = 300
def initialize(self):
'''
Model training step
'''
self.info('initialize')
self.code = 'XRP/USDT'
df = self.ctx.feed[self.code]
self.ctx.feed[self.code] = update_df(df)
self.y = get_y(df[self.num_pre_train-1:])
self.y_true = self.y.values
self.clf = KNeighborsClassifier(7)
tmp = df.dropna().astype(float)
X, y = get_clean_Xy(tmp[:self.num_pre_train])
self.clf.fit(X, y)
self.y_pred = []
def before_trade(self, order):
return True
def on_order_ok(self, order):
self.my_quantity = self.ctx.get_quantity(order['code'])
self.info(
f"{order['side'].name} order [number {order['id']}] ({order['order_type'].name}) executed [quantity {order['quantity']}] {order['code']} [price ${order['price']:2f}] [Cost ${order['cost']:2f}] [Commission: ${order['commission']}] [Available Cash: ${self.ctx.available_cash}] [Position: #{self.ctx.get_quantity(order['code'])}] [Gross P&L: ${order['pnl']}] [Net P&L: ${order['pnl_net']}]"
)
if not order['is_open']:
self.info(f"Trade closed, pnl: {order['pnl']}========")
def on_market_start(self):
# self.info('on_market_start')
pass
def on_market_close(self):
# self.info('on_market_close')
pass
def on_order_timeout(self, order):
self.info(f'on_order_timeout. Order: {order}')
pass
def finish(self):
self.info('finish')
def on_bar(self, tick):
'''
Model scoring and decisioning step
'''
bar_data = self.ctx.bar_data[self.code]
hist_data = self.ctx.hist_data[self.code]
hist_df = self.ctx.history[self.code].copy()
df = update_df(hist_df)
# print(df)
# if len(hist_data) < self.num_pre_train:
# return
bar_data = df.iloc[-1]
check_res = np.any(np.isnan(bar_data))
if check_res:
return
open, high, low, close = bar_data.open, bar_data.high, bar_data.low, bar_data.close
X = get_X(bar_data)
forecast = self.clf.predict([X])[0]
self.y_pred.append(forecast)
# self.ctx.cplot(forecast,'Forcast')
# self.ctx.cplot(self.y[tick],'Groundtruth')
# self.info(f"focasing result: {forecast}; {open}, {high}, {low}, {close}")
upper, lower = close * (1 + np.r_[1, -1]*PCT_CHANGE)
if forecast == 1 and not self.ctx.get_quantity(self.code):
o1 = self.order_amount(code=self.code,amount=self.ctx.available_cash,side=SideType.BUY, asset_type='Crypto')
self.info(f"BUY order [number {o1['id']}] created, [quantity {o1['quantity']}] [price {o1['price']}] [balance: {self.ctx.available_cash}]")
osl = self.sell(code=self.code,quantity=o1['quantity'], price=lower, stop_price=lower, asset_type='Crypto')
self.info(f"STOPLOSS order [number {osl['id']}] created, [quantity {osl['quantity']}] [price {osl['price']}] [balance: {self.ctx.available_cash}]")
elif forecast == -1 and self.ctx.get_quantity(self.code):
o2 = self.close(code=self.code, price=upper)
self.info(f"SELL order [number {o2['id']}] created, [quantity {o2['quantity']}] [price {o2['price']}] [balance: {self.ctx.available_cash}]")
Run your strategy:
if __name__ == '__main__':
# tk = 'eyJ0eXAiOi......'
# set_token(tk)
base = create_report_folder()
start_date = '2021-01-01'
end_date = '2021-07-01'
sdf = CryptoDataFeed(
symbols=['XRP/USDT', 'BTC/USDT'],
start_date=start_date,
end_date=end_date,
interval='4h',
# interval='1m',
order_ascending=True,
store_path=base
)
feed, idx = sdf.fetch_data()
benchmark = feed.pop('BTC/USDT')
broker = BackTestBroker(
commission_rate=0.001,
min_commission=None,
trade_at='close',
cash=5000,
short_cash=False,
slippage=0.0)
mytest = BackTest(
feed,
MLStrategy,
benchmark=('BTC/USDT', benchmark),
broker=broker,
tradedays=idx,
store_path=base
)
mytest.start()
Retrieve statistic results:
prefix = ''
stats = mytest.ctx.statistic.stats(pprint=True, annualization=252, risk_free=0.0442)
stats['model_name'] = 'Simple KNN Signal Generation Strategy'
stats['algorithm'] = ['KNN', 'Simple Moving Average', 'Bollinger Band']
print(stats)
mytest.contest_output(is_plot=True)
Result:
start 2021-01-01 00:00:00+08:00
end 2021-07-01 00:00:00+08:00
duration 181 days 00:00:00
trading_pairs (XRP/USDT)
benchmark BTC/USDT
beginning_balance 5000
ending_balance 9622.580449
total_net_profit 4622.580449
gross_profit 68000.370935
gross_loss -63377.790486
profit_factor 1.072937
return_on_initial_capital 0.924516
annualized_return 0.163897
total_return 0.924516
max_return 2.472148
min_return -0.000999
number_trades 398
number_winning_trades 179
number_losing_trades 65
avg_daily_trades 3.344538
avg_weekly_trades 15.920000
avg_monthly_trades 66.333333
win_ratio 0.449749
loss_ratio 0.163317
win_days 53
loss_days 34
max_win_in_day 2236.412270
max_loss_in_day -2620.335665
max_consecutive_win_days 31
max_consecutive_loss_days 2
avg_profit_per_trade 1024.975575
trading_period 0 years 6 months 0 days
avg_daily_pnl($) 4.256520
avg_daily_pnl 0.000897
avg_weekly_pnl($) 177.791556
avg_weekly_pnl 0.035889
avg_monthly_pnl($) 509.819819
avg_monthly_pnl 0.106800
avg_quarterly_pnl($) -1589.908852
avg_quarterly_pnl -0.124188
avg_annualy_pnl($) NaN
avg_annualy_pnl NaN
sharpe_ratio 0.482168
sortino_ratio 0.826390
annualized_volatility 0.377342
omega_ratio 0.021357
downside_risk 0.273549
information_ratio 0.019368
beta 0.444923
alpha -0.997702
calmar_ratio 0.340877
tail_ratio 1.295467
stability_of_timeseries 0.568724
max_drawdown 0.480809
max_drawdown_period (2021-04-12 16:00:00+08:00, 2021-06-23 04:00:0...
max_drawdown_duration 71 days 12:00:00
sqn 1.321773
model_name Simple KNN Signal Generation Strategy
algorithm [KNN, Simple Moving Average, Bollinger Band]
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Areix_IO-0.1.14.tar.gz
(1.5 MB
view hashes)