starting part 3
This commit is contained in:
@@ -0,0 +1,476 @@
|
|||||||
|
{
|
||||||
|
"cells": [
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 27,
|
||||||
|
"id": "87f54819",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"# Data and analysis libraries\n",
|
||||||
|
"import polars as pl # Fast dataframes for financial data\n",
|
||||||
|
"import numpy as np # Numerical computing library\n",
|
||||||
|
"from datetime import datetime, timedelta # Date and time operations\n",
|
||||||
|
"import random\n",
|
||||||
|
"\n",
|
||||||
|
"# Machine learning libraries \n",
|
||||||
|
"import torch # PyTorch framework\n",
|
||||||
|
"import torch.nn as nn # Neural network modules\n",
|
||||||
|
"import torch.optim as optim # Optimization algorithms\n",
|
||||||
|
"import research # Model building and training utilities\n",
|
||||||
|
"\n",
|
||||||
|
"# Visualization and \n",
|
||||||
|
"import altair as alt # Interactive visualization library\n",
|
||||||
|
"\n",
|
||||||
|
"# data sources\n",
|
||||||
|
"import binance # Binance market data utilities"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"id": "8e8cc3fb",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"# Part 3 - Implementation"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"id": "2718194d",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"## Recap"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"id": "4fd78781",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"### Part 1: Reasearch"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 28,
|
||||||
|
"id": "ced8e0ab",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"text/plain": [
|
||||||
|
"LinearModel(\n",
|
||||||
|
" (linear): Linear(in_features=3, out_features=1, bias=True)\n",
|
||||||
|
")"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"execution_count": 28,
|
||||||
|
"metadata": {},
|
||||||
|
"output_type": "execute_result"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"import models\n",
|
||||||
|
"\n",
|
||||||
|
"model = models.LinearModel(3)\n",
|
||||||
|
"model.load_state_dict(torch.load('model_weights.pth', weights_only=True))\n",
|
||||||
|
"model.eval()"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"id": "0c43665f",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"### AR(3) Model to predict future log return - 12h forecast horizon"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 29,
|
||||||
|
"id": "d26b6d5a",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "stdout",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"linear.weight:\n",
|
||||||
|
"[[-0.02849757 -0.08180149 -0.05941094]]\n",
|
||||||
|
"linear.bias:\n",
|
||||||
|
"[0.00058626]\n"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"research.print_model_params(model)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"id": "d8250c71",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"## Part 2: Strategy Recap"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 30,
|
||||||
|
"id": "13a8bba8",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"# ~14% without any optimization\n",
|
||||||
|
"# 1. Compounding Trade Sizing\n",
|
||||||
|
"# 2. Leverage\n",
|
||||||
|
"# ~14% to >40%"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"id": "4c0d6f13",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"## Tick"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 31,
|
||||||
|
"id": "8c0d69f4",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"from abc import ABC, abstractmethod\n",
|
||||||
|
"from typing import Generic, TypeVar\n",
|
||||||
|
"\n",
|
||||||
|
"T = TypeVar('T') # input type\n",
|
||||||
|
"R = TypeVar('R') # output type\n",
|
||||||
|
"\n",
|
||||||
|
"class Tick(ABC, Generic[T, R]):\n",
|
||||||
|
" @abstractmethod\n",
|
||||||
|
" def on_tick(self, val: T) -> R:\n",
|
||||||
|
" \"\"\"Handle a new tick and optionally return a result.\"\"\"\n",
|
||||||
|
" pass"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"id": "4c684fbf",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"## Sliding Window"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 32,
|
||||||
|
"id": "9d331f96",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"from collections import deque\n",
|
||||||
|
"from typing import Deque, Optional\n",
|
||||||
|
"import numpy as np\n",
|
||||||
|
"\n",
|
||||||
|
"class DequeWindow(Tick[T, Optional[T]], Generic[T]):\n",
|
||||||
|
" def __init__(self, n: int):\n",
|
||||||
|
" self._data: Deque[T] = deque(maxlen=n)\n",
|
||||||
|
"\n",
|
||||||
|
" def on_tick(self, val: T) -> Optional[T]:\n",
|
||||||
|
" \"\"\"Append a value and return the oldest value dropped (if any).\"\"\"\n",
|
||||||
|
" dropped = None\n",
|
||||||
|
" if self.is_full():\n",
|
||||||
|
" dropped = self._data[0]\n",
|
||||||
|
" self._data.append(val)\n",
|
||||||
|
" return dropped\n",
|
||||||
|
" \n",
|
||||||
|
" def is_full(self) -> bool:\n",
|
||||||
|
" return self._data.maxlen == len(self._data)\n",
|
||||||
|
"\n",
|
||||||
|
" def append_left(self, val: T) -> Optional[T]:\n",
|
||||||
|
" dropped = None\n",
|
||||||
|
" if self.is_full():\n",
|
||||||
|
" dropped = self._data[-1]\n",
|
||||||
|
" self._data.appendleft(val)\n",
|
||||||
|
" return dropped\n",
|
||||||
|
" \n",
|
||||||
|
" def to_numpy(self) -> np.ndarray:\n",
|
||||||
|
" return np.array(self._data)\n",
|
||||||
|
" \n",
|
||||||
|
" def __repr__(self) -> str:\n",
|
||||||
|
" cls_name = self.__class__.__name__\n",
|
||||||
|
" return f\"{cls_name}(capacity={self._data.maxlen}, values={list(self._data)})\""
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"id": "6b1af130",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"### Array based window"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 33,
|
||||||
|
"id": "f4fc79a5",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"import numpy as np\n",
|
||||||
|
"from typing import Optional\n",
|
||||||
|
"\n",
|
||||||
|
"class NumpyWindow(Tick[T, Optional[T]]):\n",
|
||||||
|
" def __init__(self, n: int, dtype=np.float64):\n",
|
||||||
|
" if n <= 0:\n",
|
||||||
|
" raise ValueError(\"Capacity must be positive.\")\n",
|
||||||
|
" self._capacity = n\n",
|
||||||
|
" self._data = np.zeros(n, dtype=dtype)\n",
|
||||||
|
" self._size = 0\n",
|
||||||
|
"\n",
|
||||||
|
" def on_tick(self, val: float) -> Optional[float]:\n",
|
||||||
|
" dropped = None\n",
|
||||||
|
"\n",
|
||||||
|
" if self._size < self._capacity:\n",
|
||||||
|
" self._data[self._size] = val\n",
|
||||||
|
" self._size += 1\n",
|
||||||
|
" else:\n",
|
||||||
|
" dropped = self._data[0]\n",
|
||||||
|
" # shift left in-place\n",
|
||||||
|
" for i in range(1, self._capacity):\n",
|
||||||
|
" self._data[i - 1] = self._data[i]\n",
|
||||||
|
" self._data[-1] = val\n",
|
||||||
|
"\n",
|
||||||
|
" return dropped\n",
|
||||||
|
"\n",
|
||||||
|
"\n",
|
||||||
|
" def __getitem__(self, idx: int) -> float:\n",
|
||||||
|
" \"\"\"Index access (0 = oldest).\"\"\"\n",
|
||||||
|
" if not 0 <= idx < self._size:\n",
|
||||||
|
" raise IndexError(\"Index out of range.\")\n",
|
||||||
|
" return self._data[idx]\n",
|
||||||
|
"\n",
|
||||||
|
" def __len__(self) -> int:\n",
|
||||||
|
" return self._size\n",
|
||||||
|
"\n",
|
||||||
|
" def capacity(self) -> int:\n",
|
||||||
|
" return self._capacity\n",
|
||||||
|
"\n",
|
||||||
|
" def is_full(self) -> bool:\n",
|
||||||
|
" return self._size == self._capacity\n",
|
||||||
|
"\n",
|
||||||
|
" def values(self) -> np.ndarray:\n",
|
||||||
|
" return self._data[:self._size]\n",
|
||||||
|
"\n",
|
||||||
|
" def __repr__(self) -> str:\n",
|
||||||
|
" vals = self.values().tolist()\n",
|
||||||
|
" return f\"{self.__class__.__name__}(capacity={self._capacity}, size={self._size}, values={vals})\""
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"id": "c9213b19",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"### Stream the Last Known Value"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 34,
|
||||||
|
"id": "e3b6ae13",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"class Last(Tick[T, T], Generic[T]):\n",
|
||||||
|
" def __init__(self):\n",
|
||||||
|
" self._value: Optional[T] = None\n",
|
||||||
|
"\n",
|
||||||
|
" def on_tick(self, val: T) -> Optional[T]:\n",
|
||||||
|
" self._value = val\n",
|
||||||
|
" return val\n",
|
||||||
|
"\n",
|
||||||
|
" def __repr__(self) -> str:\n",
|
||||||
|
" cls_name = self.__class__.__name__\n",
|
||||||
|
" return f\"{cls_name}(value={self._value})\""
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"id": "d93282e7",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"## Streaming Log Returns"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 35,
|
||||||
|
"id": "e14f546c",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"import numpy as np\n",
|
||||||
|
"\n",
|
||||||
|
"class LogReturn(Tick[float, Optional[float]], Generic[T]):\n",
|
||||||
|
" def __init__(self):\n",
|
||||||
|
" self._window = NumpyWindow(2)\n",
|
||||||
|
"\n",
|
||||||
|
" def on_tick(self, val: float) -> Optional[float]:\n",
|
||||||
|
" self._window.on_tick(val)\n",
|
||||||
|
" if self._window.is_full():\n",
|
||||||
|
" return np.log(self._window[1] / self._window[0])\n",
|
||||||
|
" else:\n",
|
||||||
|
" return None\n",
|
||||||
|
" \n",
|
||||||
|
" def __repr__(self) -> str:\n",
|
||||||
|
" cls_name = self.__class__.__name__\n",
|
||||||
|
" return f\"{cls_name}(window={self._window})\""
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"id": "8c2ef8a2",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"## Streaming Auto-Regressive Log Returns Lags"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 36,
|
||||||
|
"id": "cf955e23",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"class LogReturnLags(Tick[float, torch.Tensor]):\n",
|
||||||
|
" def __init__(self, no_lags: int):\n",
|
||||||
|
" self._lags = DequeWindow(no_lags)\n",
|
||||||
|
" self._log_return = LogReturn()\n",
|
||||||
|
" \n",
|
||||||
|
" def on_tick(self, val: float) -> torch.Tensor | None:\n",
|
||||||
|
" log_ret = self._log_return.on_tick(val)\n",
|
||||||
|
" if log_ret is not None:\n",
|
||||||
|
" self._lags.append_left(log_ret)\n",
|
||||||
|
" return torch.tensor(self._lags.to_numpy(), dtype=torch.float32) if self._lags.is_full() else None\n",
|
||||||
|
" else:\n",
|
||||||
|
" return None\n",
|
||||||
|
" \n",
|
||||||
|
" def __repr__(self) -> str:\n",
|
||||||
|
" cls_name = self.__class__.__name__\n",
|
||||||
|
" return f\"{cls_name}(lags={self._lags}, log_return={self._log_return})\" "
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 37,
|
||||||
|
"id": "7d62e078",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"text/plain": [
|
||||||
|
"tensor([ 0.3747, -0.3102, 0.4055])"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"execution_count": 37,
|
||||||
|
"metadata": {},
|
||||||
|
"output_type": "execute_result"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"lags = LogReturnLags(3)\n",
|
||||||
|
"lags.on_tick(90)\n",
|
||||||
|
"lags.on_tick(100)\n",
|
||||||
|
"lags.on_tick(150)\n",
|
||||||
|
"lags.on_tick(110)\n",
|
||||||
|
"features = lags.on_tick(160)\n",
|
||||||
|
"features"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"id": "8ab400f2",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"### Streaming features into our model"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 38,
|
||||||
|
"id": "43b202c4",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"text/plain": [
|
||||||
|
"tensor([-0.0088])"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"execution_count": 38,
|
||||||
|
"metadata": {},
|
||||||
|
"output_type": "execute_result"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"X = features\n",
|
||||||
|
"with torch.no_grad():\n",
|
||||||
|
" y_hat = model(X)\n",
|
||||||
|
"y_hat"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 39,
|
||||||
|
"id": "d57f4837",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"text/plain": [
|
||||||
|
"tensor(-0.0088)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"execution_count": 39,
|
||||||
|
"metadata": {},
|
||||||
|
"output_type": "execute_result"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"y_hat[0]"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"id": "a1f09327",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"## Build the Trading System"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"kernelspec": {
|
||||||
|
"display_name": "study (3.14.5.final.0)",
|
||||||
|
"language": "python",
|
||||||
|
"name": "python3"
|
||||||
|
},
|
||||||
|
"language_info": {
|
||||||
|
"codemirror_mode": {
|
||||||
|
"name": "ipython",
|
||||||
|
"version": 3
|
||||||
|
},
|
||||||
|
"file_extension": ".py",
|
||||||
|
"mimetype": "text/x-python",
|
||||||
|
"name": "python",
|
||||||
|
"nbconvert_exporter": "python",
|
||||||
|
"pygments_lexer": "ipython3",
|
||||||
|
"version": "3.14.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nbformat": 4,
|
||||||
|
"nbformat_minor": 5
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user