| Overall Statistics |
|
Total Trades 7769 Average Win 0.23% Average Loss -0.14% Compounding Annual Return 214.523% Drawdown 51.900% Expectancy 0.301 Net Profit 422.980% Sharpe Ratio 2.802 Probabilistic Sharpe Ratio 82.302% Loss Rate 50% Win Rate 50% Profit-Loss Ratio 1.62 Alpha 0.927 Beta 0.656 Annual Standard Deviation 0.544 Annual Variance 0.296 Information Ratio 1.446 Tracking Error 0.425 Treynor Ratio 2.323 Total Fees $584617.03 |
import torch
from ddpg_agent import Agent
import numpy as np
import pandas as pd
import pickle
from io import BytesIO
from collections import deque
import base64
class NadionTransdimensionalAtmosphericScrubbers(QCAlgorithm):
def Initialize(self):
self.window = 64
self.channels = 24
self.SetCash(100000)
self.agent = Agent(num_channels = self.channels, state_size=self.window, action_size=1, random_seed=101)
act_b64_str = self.Download("https://www.dropbox.com/s/70iodbiktu4ewaw/b64_actor_ep836_checkpoint.pth?dl=1")
cri_b64_str = self.Download("https://www.dropbox.com/s/z84uwl2gmb9kpvl/b64_critic_ep836_checkpoint.pth?dl=1")
# String Encode to bytes
act_b = act_b64_str.encode("UTF-8")
# Decoding the Base64 bytes
act_d = base64.b64decode(act_b)
# String Encode to bytes
cri_b = cri_b64_str.encode("UTF-8")
# Decoding the Base64 bytes
cri_d = base64.b64decode(cri_b)
self.agent.actor_local.load_state_dict(torch.load(BytesIO(act_d), map_location=lambda storage, loc: storage))
self.agent.critic_local.load_state_dict(torch.load(BytesIO(cri_d), map_location=lambda storage, loc: storage))
self.symbolList = ["BTCUSD","ETHUSD","LTCUSD","BCHUSD"]
self.rollingWindow = {}
self.weights = {}
self.min_oder = {"BTCUSD":0.01,"ETHUSD":0.01,"LTCUSD":0.1,"XRPUSD":1,"BCHUSD":0.01,"EOSUSD":0.1}
for name in self.symbolList:
self.AddCrypto(name, Resolution.Minute, Market.GDAX)
length = self.window+1
self.rollingWindow["close_15min_{0}".format(name)] = deque(maxlen=length)
self.rollingWindow["close_30min_{0}".format(name)] = deque(maxlen=length)
self.rollingWindow["close_1hr_{0}".format(name)] = deque(maxlen=length)
self.rollingWindow["close_2hr_{0}".format(name)] = deque(maxlen=length)
self.rollingWindow["close_4hr_{0}".format(name)] = deque(maxlen=length)
self.rollingWindow["close_8hr_{0}".format(name)] = deque(maxlen=length)
self.rollingWindow["close_16hr_{0}".format(name)] = deque(maxlen=length)
self.rollingWindow["close_32hr_{0}".format(name)] = deque(maxlen=length)
self.rollingWindow["volume_15min_{0}".format(name)] = deque(maxlen=length)
self.rollingWindow["volume_30min_{0}".format(name)] = deque(maxlen=length)
self.rollingWindow["volume_1hr_{0}".format(name)] = deque(maxlen=length)
self.rollingWindow["volume_2hr_{0}".format(name)] = deque(maxlen=length)
self.rollingWindow["volume_4hr_{0}".format(name)] = deque(maxlen=length)
self.rollingWindow["volume_8hr_{0}".format(name)] = deque(maxlen=length)
self.rollingWindow["volume_16hr_{0}".format(name)] = deque(maxlen=length)
self.rollingWindow["volume_32hr_{0}".format(name)] = deque(maxlen=length)
# https://www.quantconnect.com/tutorials/api-tutorials/consolidating-data-to-build-bars
fifteenConsolidator = TradeBarConsolidator(timedelta(minutes=15))
fifteenConsolidator.DataConsolidated += self.FifteenConsolidator
self.SubscriptionManager.AddConsolidator(name, fifteenConsolidator)
thirtyConsolidator = TradeBarConsolidator(timedelta(minutes=30))
thirtyConsolidator.DataConsolidated += self.ThirtyConsolidator
self.SubscriptionManager.AddConsolidator(name, thirtyConsolidator)
oneHourConsolidator = TradeBarConsolidator(timedelta(hours=1))
oneHourConsolidator.DataConsolidated += self.OneHourConsolidator
self.SubscriptionManager.AddConsolidator(name, oneHourConsolidator)
twoHourConsolidator = TradeBarConsolidator(timedelta(hours=2))
twoHourConsolidator.DataConsolidated += self.TwoHourConsolidator
self.SubscriptionManager.AddConsolidator(name, twoHourConsolidator)
fourHourConsolidator = TradeBarConsolidator(timedelta(hours=4))
fourHourConsolidator.DataConsolidated += self.FourHourConsolidator
self.SubscriptionManager.AddConsolidator(name, fourHourConsolidator)
eightHourConsolidator = TradeBarConsolidator(timedelta(hours=8))
eightHourConsolidator.DataConsolidated += self.EightHourConsolidator
self.SubscriptionManager.AddConsolidator(name, eightHourConsolidator)
sixteenHourConsolidator = TradeBarConsolidator(timedelta(hours=16))
sixteenHourConsolidator.DataConsolidated += self.SixteenHourConsolidator
self.SubscriptionManager.AddConsolidator(name, sixteenHourConsolidator)
thirdytwoHourConsolidator = TradeBarConsolidator(timedelta(hours=32))
thirdytwoHourConsolidator.DataConsolidated += self.ThirdytwoHourConsolidator
self.SubscriptionManager.AddConsolidator(name, thirdytwoHourConsolidator)
self.SetBrokerageModel(BrokerageName.GDAX, AccountType.Cash)
self.SetStartDate(2019, 1, 1)
self.SetBenchmark("BTCUSD")
self.SetWarmUp(int((self.window+1)*64*60))
def std(self, x):
y = (x - x.mean()) / x.std()
return y
def Features(self, symbol):
obs = np.array([])
a = np.diff(np.log(np.array(self.rollingWindow["close_15min_{0}".format(symbol)]))) / 0.008
aa = self.std(np.array(self.rollingWindow["close_15min_{0}".format(symbol)])[:-1])
aaa = self.std(np.array(self.rollingWindow["volume_15min_{0}".format(symbol)])[:-1])
b = np.diff(np.log(np.array(self.rollingWindow["close_30min_{0}".format(symbol)]))) / 0.008 / 2
bb = self.std(np.array(self.rollingWindow["close_30min_{0}".format(symbol)])[:-1])
bbb = self.std(np.array(self.rollingWindow["volume_30min_{0}".format(symbol)])[:-1])
c = np.diff(np.log(np.array(self.rollingWindow["close_1hr_{0}".format(symbol)]))) / 0.008 / 3
cc = self.std(np.array(self.rollingWindow["close_1hr_{0}".format(symbol)])[:-1])
ccc = self.std(np.array(self.rollingWindow["volume_1hr_{0}".format(symbol)])[:-1])
d = np.diff(np.log(np.array(self.rollingWindow["close_2hr_{0}".format(symbol)]))) / 0.008 / 4
dd = self.std(np.array(self.rollingWindow["close_2hr_{0}".format(symbol)])[:-1])
ddd = self.std(np.array(self.rollingWindow["volume_2hr_{0}".format(symbol)])[:-1])
e = np.diff(np.log(np.array(self.rollingWindow["close_4hr_{0}".format(symbol)]))) / 0.008 / 5
ee = self.std(np.array(self.rollingWindow["close_4hr_{0}".format(symbol)])[:-1])
eee = self.std(np.array(self.rollingWindow["volume_4hr_{0}".format(symbol)])[:-1])
f = np.diff(np.log(np.array(self.rollingWindow["close_8hr_{0}".format(symbol)]))) / 0.008 / 6
ff = self.std(np.array(self.rollingWindow["close_8hr_{0}".format(symbol)])[:-1])
fff = self.std(np.array(self.rollingWindow["volume_8hr_{0}".format(symbol)])[:-1])
g = np.diff(np.log(np.array(self.rollingWindow["close_16hr_{0}".format(symbol)]))) / 0.008 / 7
gg = self.std(np.array(self.rollingWindow["close_16hr_{0}".format(symbol)])[:-1])
ggg = self.std(np.array(self.rollingWindow["volume_16hr_{0}".format(symbol)])[:-1])
h = np.diff(np.log(np.array(self.rollingWindow["close_32hr_{0}".format(symbol)]))) / 0.008 / 8
hh = self.std(np.array(self.rollingWindow["close_32hr_{0}".format(symbol)])[:-1])
hhh = self.std(np.array(self.rollingWindow["volume_32hr_{0}".format(symbol)])[:-1])
obs = np.concatenate((a,aa,aaa,b,bb,bbb,c,cc,ccc,d,dd,ddd,e,ee,eee,f,ff,fff,g,gg,ggg,h,hh,hhh), axis=0)
obs[np.isnan(obs)] = 0
obs[np.isinf(obs)] = 0
obs[np.isneginf(obs)] = 0
state = np.asarray(obs)
return state.reshape(1, self.channels, self.window)
def normalize(self,x):
return np.round((x+1)/2*0.25,2)
def FifteenConsolidator(self, sender, bar):
self.rollingWindow["close_15min_{0}".format(bar.Symbol)].appendleft(bar.Close)
self.rollingWindow["volume_15min_{0}".format(bar.Symbol)].appendleft(bar.Volume)
symbol = str(bar.Symbol)
Close = bar.Close
Volume = bar.Volume
if not self.IsWarmingUp and len(self.rollingWindow["close_32hr_{0}".format(bar.Symbol)]) >= 33:
# Check current portfolio for ratio
if self.Securities[symbol].Invested:
currentweight = (self.Portfolio[symbol].Quantity * Close) /self.Portfolio.TotalPortfolioValue
else:
currentweight = 0.0
self.weights[symbol] = currentweight
state = self.Features(symbol)
action = self.agent.act(state)
weight = round(float(self.normalize(action[0])),2)
self.Debug("sym {} weight {} current {}".format(symbol, weight, currentweight))
if weight >= self.weights[symbol] + 0.05:
self.SetHoldings(symbol, weight)
elif weight <= self.weights[symbol] - 0.05 or weight == 0:
self.SetHoldings(symbol, weight)
else:
pass
def ThirtyConsolidator(self, sender, bar):
self.rollingWindow["close_30min_{0}".format(bar.Symbol)].appendleft(bar.Close)
self.rollingWindow["volume_30min_{0}".format(bar.Symbol)].appendleft(bar.Volume)
def OneHourConsolidator(self, sender, bar):
self.rollingWindow["close_1hr_{0}".format(bar.Symbol)].appendleft(bar.Close)
self.rollingWindow["volume_1hr_{0}".format(bar.Symbol)].appendleft(bar.Volume)
def TwoHourConsolidator(self, sender, bar):
self.rollingWindow["close_2hr_{0}".format(bar.Symbol)].appendleft(bar.Close)
self.rollingWindow["volume_2hr_{0}".format(bar.Symbol)].appendleft(bar.Volume)
def FourHourConsolidator(self, sender, bar):
self.rollingWindow["close_4hr_{0}".format(bar.Symbol)].appendleft(bar.Close)
self.rollingWindow["volume_4hr_{0}".format(bar.Symbol)].appendleft(bar.Volume)
def EightHourConsolidator(self, sender, bar):
self.rollingWindow["close_8hr_{0}".format(bar.Symbol)].appendleft(bar.Close)
self.rollingWindow["volume_8hr_{0}".format(bar.Symbol)].appendleft(bar.Volume)
def SixteenHourConsolidator(self, sender, bar):
self.rollingWindow["close_16hr_{0}".format(bar.Symbol)].appendleft(bar.Close)
self.rollingWindow["volume_16hr_{0}".format(bar.Symbol)].appendleft(bar.Volume)
def ThirdytwoHourConsolidator(self, sender, bar):
self.rollingWindow["close_32hr_{0}".format(bar.Symbol)].appendleft(bar.Close)
self.rollingWindow["volume_32hr_{0}".format(bar.Symbol)].appendleft(bar.Volume)
def OnData(self, data):
pass
def OnOrderEvent(self, orderEvent):
self.Debug("{} {}".format(self.Time, orderEvent.ToString()))
def OnEndOfAlgorithm(self):
self.Log("{} - TotalPortfolioValue: {}".format(self.Time, self.Portfolio.TotalPortfolioValue))
self.Log("{} - CashBook: {}".format(self.Time, self.Portfolio.CashBook))import numpy as np
from model import Actor, Critic
import torch
import torch.optim as optim
WEIGHT_DECAY = 0.000
device = torch.device("cpu")
class Agent():
def __init__(self, num_channels, state_size, action_size, random_seed):
self.state_size = state_size
self.action_size = action_size
self.seed = 0
# Actor Network (w/ Target Network)
self.actor_local = Actor(num_channels, state_size, action_size, random_seed).to(device)
self.actor_target = Actor(num_channels, state_size, action_size, random_seed).to(device)
# Critic Network (w/ Target Network)
self.critic_local = Critic(num_channels, state_size, action_size, random_seed).to(device)
self.critic_target = Critic(num_channels, state_size, action_size, random_seed).to(device)
def act(self, state):
"""Returns actions for given state as per current policy."""
state = torch.from_numpy(state).float().to(device)
self.actor_local.eval()
with torch.no_grad():
action = self.actor_local(state).cpu().data.numpy()
return np.clip(action, -1, 1)import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
def hidden_init(layer):
fan_in = layer.weight.data.size()[0]
lim = 1. / np.sqrt(fan_in)
return (-lim, lim)
class Actor(nn.Module):
"""Actor (Policy) Model."""
def __init__(self, num_channels, state_size, action_size, seed, conv1=100, conv2=100, fc1_units=200, kernel_size=10, stride=1):
super(Actor, self).__init__()
self.seed = torch.manual_seed(seed)
self.conv1 = nn.Conv1d(in_channels=num_channels, out_channels=conv1, kernel_size=kernel_size, stride=stride)
self.conv2 = nn.Conv1d(in_channels=conv1, out_channels=conv2, kernel_size=kernel_size, stride=stride)
self.poolAvg = nn.AvgPool1d(kernel_size=3)
self.dropout = nn.Dropout(0.25)
def conv1d_size_out(size):
return (size - (kernel_size - 1) - 1) // stride + 1
convw = conv1d_size_out(conv1d_size_out(state_size)//3)//3
self.linear_input_size = convw * conv2 #Make sure its convw x last conv!
self.fc1 = nn.Linear(self.linear_input_size, fc1_units)
self.fc2 = nn.Linear(fc1_units, action_size)
self.reset_parameters()
def reset_parameters(self):
self.conv1.weight.data.uniform_(*hidden_init(self.conv1))
self.conv2.weight.data.uniform_(*hidden_init(self.conv2))
self.fc1.weight.data.uniform_(*hidden_init(self.fc1))
self.fc2.weight.data.uniform_(-3e-3, 3e-3)
def forward(self, state):
"""Build an actor (policy) network that maps states -> actions."""
x = F.relu(self.conv1(state))
x = self.poolAvg(x)
x = F.relu(self.conv2(x))
x = self.poolAvg(x)
x = x.view(-1, self.linear_input_size)
x = F.relu(self.fc1(x))
return torch.tanh(self.fc2(x))
class Critic(nn.Module):
"""Critic (Value) Model."""
def __init__(self, num_channels, state_size, action_size, seed, conv1=100, conv2=100, fcs1_units=200, kernel_size=10, stride=1):
super(Critic, self).__init__()
self.seed = torch.manual_seed(seed)
self.conv1 = nn.Conv1d(in_channels=num_channels, out_channels=conv1, kernel_size=kernel_size, stride=stride)
self.conv2 = nn.Conv1d(in_channels=conv1, out_channels=conv2, kernel_size=kernel_size, stride=stride)
self.poolAvg = nn.AvgPool1d(kernel_size=3)
self.dropout = nn.Dropout(0.25)
def conv1d_size_out(size):
return (size - (kernel_size - 1) - 1) // stride + 1
convw = conv1d_size_out(conv1d_size_out(state_size)//3)//3
self.linear_input_size = convw * conv2 #Make sure its convw x last conv!
self.fcs1 = nn.Linear(self.linear_input_size+ action_size, fcs1_units)
self.fc2 = nn.Linear(fcs1_units, 1)
self.reset_parameters()
def reset_parameters(self):
self.conv1.weight.data.uniform_(*hidden_init(self.conv1))
self.conv2.weight.data.uniform_(*hidden_init(self.conv2))
self.fcs1.weight.data.uniform_(*hidden_init(self.fcs1))
self.fc2.weight.data.uniform_(-3e-3, 3e-3)
def forward(self, state, action):
"""Build a critic (value) network that maps (state, action) pairs -> Q-values."""
x = F.relu(self.conv1(state))
x = self.poolAvg(x)
x = F.relu(self.conv2(x))
x = self.poolAvg(x)
xs = x.view(-1, self.linear_input_size)
x = torch.cat([xs, action], dim=1)
x = F.relu(self.fcs1(x))
return self.fc2(x)