Examples#

Strategy Submission Snippet#

Strategy Submission Snippet in Vector:#

import pandas as pd
from enum import Enum

class TradeType(Enum):
    LONG = "LONG"
    SHORT = "SHORT"
    REVERSE_LONG = "REVERSE_LONG"
    REVERSE_SHORT = "REVERSE_SHORT"
    CLOSE = "CLOSE"
    HOLD = "HOLD"

class Strategy:
    def run(self, data: pd.DataFrame) -> pd.DataFrame:
        """
        Execute the strategy on the provided data.

        Args:
            data (pd.DataFrame): Input DataFrame with OHLCV data.

                Expected columns and their data types:
                - datetime: datetime64[ns]
                - open: float64
                - high: float64
                - low: float64
                - close: float64
                - volume: float64

        Returns:
            pd.DataFrame: DataFrame with added indicators and trade types.

            Required columns:
                - trade_type: str (LONG, SHORT, CLOSE, REVERSE_LONG, REVERSE_SHORT, HOLD)

            Optional columns:
                - TP: float64 (Take Profit)
                - SL: float64 (Stop Loss)
                - TSL: float64 (Trailing Stop Loss)
        """
        # Implement your strategy logic here
        return data

Strategy Submission Snippet in Jupyter:#

Snippet#
  1 """
  2 All modules and functions required for back_test should be added in requirements.txt.
  3 """
  4 import uuid
  5 import pandas as pd
  6 from untrade.client import Client
  7
  8 # ALL your imports here
  9
 10
 11 def process_data(data):
 12     """
 13     Process the input data and return a dataframe with all the necessary indicators and data for making signals.
 14
 15     Parameters:
 16     data (pandas.DataFrame): The input data to be processed.
 17
 18     Returns:
 19     pandas.DataFrame: The processed dataframe with all the necessary indicators and data.
 20     """
 21     return data
 22
 23
 24 # -------STRATEGY LOGIC--------#
 25 def strat(data):
 26     """
 27     Create a strategy based on indicators or other factors.
 28
 29     Parameters:
 30     - data: DataFrame
 31         The input data containing the necessary columns for strategy creation.
 32
 33     Returns:
 34     - DataFrame
 35         The modified input data with an additional 'signal' column representing the strategy signals.
 36     """
 37     return data
 38
 39
 40 def perform_backtest(csv_file_path):
 41     client = Client()
 42     result = client.backtest(
 43         jupyter_id="Your User",  # the one you use to login to jupyter.untrade.io
 44         file_path=csv_file_path,
 45         leverage=1,  # Adjust leverage as needed
 46     )
 47     return result
 48
 49 # Following function can be used for every size of file, specially for large files(time consuming,depends on upload speed and file size)
 50 def perform_backtest_large_csv(csv_file_path):
 51     client = Client()
 52     file_id = str(uuid.uuid4())
 53     chunk_size = 90 * 1024 * 1024
 54     total_size = os.path.getsize(csv_file_path)
 55     total_chunks = (total_size + chunk_size - 1) // chunk_size
 56     chunk_number = 0
 57     if total_size <= chunk_size:
 58         total_chunks = 1
 59         # Normal Backtest
 60         result = client.backtest(
 61             file_path=csv_file_path,
 62             leverage=1,
 63             jupyter_id="test",
 64             # result_type="Q",
 65         )
 66         for value in result:
 67             print(value)
 68
 69         return result
 70
 71     with open(csv_file_path, "rb") as f:
 72         while True:
 73             chunk_data = f.read(chunk_size)
 74             if not chunk_data:
 75                 break
 76             chunk_file_path = f"/tmp/{file_id}_chunk_{chunk_number}.csv"
 77             with open(chunk_file_path, "wb") as chunk_file:
 78                 chunk_file.write(chunk_data)
 79
 80             # Large CSV Backtest
 81             result = client.backtest(
 82                 file_path=chunk_file_path,
 83                 leverage=1,
 84                 jupyter_id="test",
 85                 file_id=file_id,
 86                 chunk_number=chunk_number,
 87                 total_chunks=total_chunks,
 88                 # result_type="Q",
 89             )
 90
 91             for value in result:
 92                 print(value)
 93
 94             os.remove(chunk_file_path)
 95
 96             chunk_number += 1
 97
 98     return result
 99
100 def main():
101     data = pd.read_csv("data/2018-22/YOUR CSV")
102
103     processed_data = process_data(data)
104
105     result_data = strat(processed_data)
106
107     csv_file_path = "results.csv"
108
109     result_data.to_csv(csv_file_path, index=False)
110
111     backtest_result = perform_backtest_large_csv(csv_file_path)
112     # backtest_result = perform_backtest(csv_file_path)
113
114     # No need to use following code if you are using perform_backtest_large_csv
115     print(backtest_result)
116     for value in backtest_result:
117         print(value)
118
119
120 if __name__ == "__main__":
121     main()

The following sections provide practical examples of how to use the UntradeSDK and Jupyter for various trading strategies and data processing tasks.

Example 1: CSV Sample#

The CSV data below represents a sample dataset that includes date and time, open, high, low, close, volume, and signals, which are used to make trading decisions as described.

  • If you are willing to open a long trade and cut it: then your entry is 1 and exit is -1.

  • If you are willing to open a short trade and cut it: then your entry is -1 and exit is 1.

  • When there is no signal - mark the timestamps with 0.

  • In between entry (1 or -1) and exit (1 or -1) of a trade, mark the timestamps with 0.

  • Datetime format should only be YYYY-MM-DD HH:MM:SS

Sample CSV Data#

datetime

open

high

low

close

volume

signals

2023-12-27 23:30:00

43022.0

43215.0

42928.75

43183.98

1202.68526

0

2023-12-28 00:30:00

43183.99

43195.0

43000.0

43154.34

1086.81276

0

2023-12-28 01:30:00

43154.34

43565.45

43121.45

43445.32

2427.3704

0

2023-12-28 02:30:00

43445.32

43484.0

43252.72

43366.2

1459.94871

0

2023-12-28 03:30:00

43366.2

43677.0

43280.33

43323.81

2065.52137

0

2023-12-28 04:30:00

43323.81

43467.74

43323.8

43428.85

1490.05628

0

2023-12-28 05:30:00

43428.86

43787.57

43383.66

43566.5

2840.455

-1

2023-12-28 06:30:00

43566.5

43718.46

43418.38

43426.0

1365.83974

0

2023-12-28 07:30:00

43426.0

43466.06

43304.27

43358.31

1073.01825

1

2023-12-28 08:30:00

43358.32

43416.16

43314.0

43408.41

803.14564

0

2023-12-28 09:30:00

43408.4

43624.74

43313.72

43314.0

1305.13865

0

2023-12-28 10:30:00

43314.01

43369.19

43140.88

43222.01

1486.04342

0

2023-12-28 11:30:00

43222.0

43222.01

42973.44

43031.97

1264.98709

0

2023-12-28 12:30:00

43031.96

43068.0

42794.11

43049.17

2159.62778

0

2023-12-28 13:30:00

43049.17

43071.1

42914.86

42931.1

1221.96638

0

2023-12-28 14:30:00

42931.1

43099.5

42868.36

43045.06

1321.33122

0

2023-12-28 15:30:00

43045.05

43192.17

43014.0

43101.71

927.7383

-1

Note If you want to use target and stoploss or either one of them then you can create your data in this format:

Sample CSV Data with Target and Stoploss#

datetime

open

high

low

close

TP

SL

signals

2023-01-04 05:45:00

16668.56

16676.32

16663.1

16675.14

0.0

0.0

0

2023-01-04 06:00:00

16675.14

16676.58

16656.76

16658.87

16623.44

16676.58

-1

2023-01-04 06:15:00

16658.65

16663.74

16652.66

16660.53

0.0

0.0

0

2023-01-04 06:30:00

16660.53

16725.0

16657.68

16697.25

16776.39

16657.68

1

2023-01-04 06:45:00

16697.25

16737.23

16690.75

16711.5

0.0

0.0

0

Example 2: Download CSV Data - CUSTOM#

Example 2 - Download CSV Data#
1 # Disabled CURRENTLY

Example 3: Srategy Implementation - Technical Indicators#

Example 3 - Srategy Implementation - Technical Indicators#
 1 import pandas as pd
 2 from untrade.client import Client
 3 from pprint import pprint
 4
 5 # ---------GLOBAL VARS------#
 6 COIN = ""
 7 PAIR = ""
 8 TIMEFRAME = ""
 9
10 def process_data(data, short_window=15, long_window=30):
11     # Assuming 'data' is a DataFrame
12     data["short_mavg"] = (
13         data["close"].rolling(window=short_window, min_periods=1, center=False).mean()
14     )
15     data["long_mavg"] = (
16         data["close"].rolling(window=long_window, min_periods=1, center=False).mean()
17     )
18     return data
19
20
21 def strat(data):
22     data["Signal"] = np.where(
23         data["short_mavg"] > data["long_mavg"],
24         1,
25         np.where(data["short_mavg"] < data["long_mavg"], -1, 0),
26     )
27
28     # Remove consecutive duplicates from signals
29     data["signals"] = data["Signal"].diff().fillna(0)
30     data["signals"] = np.where(data["signals"] != 0, data["Signal"], 0)
31
32     # Randomize the signals
33     data["signals"] = data["signals"].apply(
34         lambda x: (
35             np.random.choice([1, 2])
36             if x == 1
37             else (np.random.choice([-1, -2]) if x == -1 else 0)
38         )
39     )
40
41     # Set target prices and stop-loss levels
42     data["tp"] = np.where(
43         data["signals"] > 0, data["close"] * 1.02, 0
44     )  # target price is 2% above the close price for long positions
45     data["sl"] = np.where(
46         data["signals"] < 0, data["close"] * 0.98, 0
47     )  # stop loss is 2% below the close price for long positions
48
49     # Clean up intermediate columns
50     data.drop(columns=["Signal"], inplace=True)
51
52     return data
53
54 def main():
55     # data = SOURCE LIVE OR HISTORICAL DATA
56
57     processed_data = process_data(data)
58
59     result_data = strat(processed_data)
60
61     return result_data
62 if __name__ == "__main__":
63     main()

Example 4: Strategy Implementation - ML#

Example 4 - Strategy Implementation - ML#
  1 # Importing necessary libraries
  2 import pandas as pd
  3 import numpy as np
  4 from sklearn.model_selection import train_test_split
  5 from sklearn.preprocessing import StandardScaler
  6 from sklearn.ensemble import RandomForestClassifier
  7 from sklearn.metrics import accuracy_score, classification_report
  8 import joblib
  9 from untrade.client import Client
 10
 11 # Constants
 12 DATA_PATH = "data/example_dataset.csv"  # Path to your dataset
 13 MODEL_PATH = "models/my_model.joblib"  # Path to save your trained model
 14
 15 # Function to load data
 16 def load_data(path):
 17     return pd.read_csv(path)
 18
 19
 20 # Function to preprocess data
 21 def preprocess_data(df):
 22     # Basic preprocessing steps (example)
 23
 24     return df
 25
 26
 27 # Function to train the model
 28 def train_model(X_train, y_train):
 29     # Train your model here
 30
 31     return model, scaler
 32
 33
 34 # Function to save the model
 35 def save_model(model, scaler, path):
 36     # Save model and scaler
 37
 38     return
 39
 40
 41 # Function to load the model
 42 def load_model(path):
 43
 44     return joblib.load(path)
 45
 46
 47 # Function to generate backtest result
 48 def backtest(X_test):
 49     """
 50     Perform backtesting using the untrade SDK.
 51
 52     Parameters:
 53     - csv_file_path (str): Path to the CSV file containing historical price data and signals.
 54
 55     Returns:
 56     - result (generator): Generator object that yields backtest results.
 57     """
 58     # Create an instance of the untrade client
 59     client = Client()
 60
 61     # Perform backtest using the provided CSV file path
 62     result = client.backtest(
 63         jupyter_id="Your_Jupyter_ID",  # the one you use to login to jupyter.untrade.io
 64         file = X_test,
 65         leverage=1,  # Adjust leverage as needed
 66         result_type="Q", # Optional
 67     )
 68
 69     return result
 70
 71
 72 # Main script
 73 if __name__ == "__main__":
 74     # Load data
 75     data = load_data(DATA_PATH)
 76
 77     # Preprocess data
 78     data = preprocess_data(data)
 79
 80     # Split data into features and target
 81     X = data.drop("target", axis=1)  # Adjust to your dataset
 82     y = data["target"]  # Adjust to your dataset
 83
 84     # Split data into training and testing sets
 85     X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
 86
 87     # Train the model
 88     model, scaler = train_model(X_train, y_train)
 89
 90     # Save the model
 91     save_model(model, scaler, MODEL_PATH)
 92
 93     # Load the model (just to confirm it works)
 94     loaded_model_data = load_model(MODEL_PATH)
 95     loaded_model = loaded_model_data["model"]
 96     loaded_scaler = loaded_model_data["scaler"]
 97
 98     # Test the model
 99     X_test_scaled = loaded_scaler.transform(X_test)
100     y_pred = loaded_model.predict(X_test_scaled)
101
102     # Evaluate the model
103     accuracy = accuracy_score(y_test, y_pred)
104     report = classification_report(y_test, y_pred)
105
106     print("Model Accuracy:", accuracy)
107     print("Classification Report:\n", report)

Example 5: Dynamic position and leverage#

import talib as ta
import pandas as pd
import numpy as np
from enum import Enum


class TradeType(Enum):
    LONG = "LONG"
    SHORT = "SHORT"
    REVERSE_LONG = "REVERSE_LONG"
    REVERSE_SHORT = "REVERSE_SHORT"
    CLOSE = "CLOSE"
    HOLD = "HOLD"


class Strategy:
    def run(self, df: pd.DataFrame) -> pd.DataFrame:
        df = self.calculate_indicators(df)
        df = self.generate_signals(df)
        return df

    def calculate_indicators(self, df):
        df["tenkan_sen"] = (
            ta.MAX(df["high"], timeperiod=9) + ta.MIN(df["low"], timeperiod=9)
        ) / 2
        df["kijun_sen"] = (
            ta.MAX(df["high"], timeperiod=26) + ta.MIN(df["low"], timeperiod=26)
        ) / 2
        df["senkou_span_a"] = ((df["tenkan_sen"] + df["kijun_sen"]) / 2).shift(26)
        df["senkou_span_b"] = (
            ta.MAX(df["high"], timeperiod=52) + ta.MIN(df["low"], timeperiod=52)
        ) / 2
        df["chikou_span"] = df["close"].shift(-26)
        return df

    def generate_signals(self, df):
        df["trade_type"] = TradeType.HOLD.value
        df["Position"] = 0
        df["_leverage"] = 0.0
        df["_position_size"] = 0.0

        bullish_signal = (
            (df["tenkan_sen"] > df["kijun_sen"])
            & (df["close"] > df["senkou_span_a"])
            & (df["close"] > df["senkou_span_b"])
            & (df["Position"].shift().fillna(0) == 0)
        )

        bearish_signal = (
            (df["tenkan_sen"] < df["kijun_sen"])
            & (df["close"] < df["senkou_span_a"])
            & (df["close"] < df["senkou_span_b"])
            & (df["Position"].shift().fillna(0) == 0)
        )

        exit_long = (df["Position"].shift().fillna(0) == 1) & (
            df["tenkan_sen"] < df["kijun_sen"]
        )
        exit_short = (df["Position"].shift().fillna(0) == -1) & (
            df["tenkan_sen"] > df["kijun_sen"]
        )

        df.loc[bullish_signal, "Position"] = 1
        df.loc[bullish_signal, "_leverage"] = 2.0
        df.loc[bullish_signal, "_position_size"] = 70

        df.loc[bearish_signal, "Position"] = -1
        df.loc[bearish_signal, "_leverage"] = 1.0
        df.loc[bearish_signal, "_position_size"] = 30

        df.loc[exit_long | exit_short, "Position"] = 0
        df.loc[exit_long | exit_short, "_leverage"] = 0.0
        df.loc[exit_long | exit_short, "_position_size"] = 0.0

        df["Position"] = df["Position"].ffill().fillna(0)
        df["_leverage"] = df["_leverage"].ffill().fillna(0.0)
        df["_position_size"] = df["_position_size"].ffill().fillna(0.0)

        position_change = df["Position"].diff().fillna(df["Position"])
        prev_position = df["Position"].shift().fillna(0)

        entry_long = position_change == 1
        df.loc[entry_long, "trade_type"] = np.where(
            prev_position[entry_long] == -1,
            TradeType.REVERSE_LONG.value,
            TradeType.LONG.value,
        )

        entry_short = position_change == -1
        df.loc[entry_short, "trade_type"] = np.where(
            prev_position[entry_short] == 1,
            TradeType.REVERSE_SHORT.value,
            TradeType.SHORT.value,
        )

        exit_positions = (position_change != 0) & (df["Position"] == 0)
        df.loc[exit_positions, "trade_type"] = TradeType.CLOSE.value

        leverage = df["_leverage"].copy()
        position_size = df["_position_size"].copy()

        mask = ~df["trade_type"].isin([TradeType.LONG.value, TradeType.SHORT.value])
        leverage[mask] = 0
        position_size[mask] = 0

        df.drop(
            columns=[
                "Position",
                "tenkan_sen",
                "kijun_sen",
                "senkou_span_a",
                "senkou_span_b",
                "chikou_span",
                "_leverage",
                "_position_size",
            ],
            inplace=True,
        )

        df["leverage"] = leverage
        df["position"] = position_size

        return df

Example 6: Two symbols with the same timeframe#

import pandas as pd
import numpy as np
import talib as ta
from enum import Enum

class TradeType(Enum):
    LONG = "LONG"
    SHORT = "SHORT"
    REVERSE_LONG = "REVERSE_LONG"
    REVERSE_SHORT = "REVERSE_SHORT"
    CLOSE = "CLOSE"
    HOLD = "HOLD"

class Strategy:
    def run(self, data_btcusdt_1d: pd.DataFrame, data_ethusdt_1d: pd.DataFrame ) -> pd.DataFrame:
        data_btcusdt_1d = data_btcusdt_1d.copy()
        data_ethusdt_1d = data_ethusdt_1d.copy()

        data_btcusdt_1d["datetime"] = pd.to_datetime(data_btcusdt_1d["datetime"])
        data_ethusdt_1d["datetime"] = pd.to_datetime(data_ethusdt_1d["datetime"])

        df = self.calculate_indicators(data_btcusdt_1d, data_ethusdt_1d)
        df = self.generate_signals(df)

        return df

    def calculate_indicators(self, btc_df, eth_df):
        btc_columns = {
            'open': 'btc_open',
            'high': 'btc_high',
            'low': 'btc_low',
            'close': 'btc_close',
            'volume': 'btc_volume'
        }
        btc_df = btc_df.rename(columns=btc_columns)

        eth_df["rsi"] = ta.RSI(eth_df["close"], timeperiod=14)
        eth_df["upper_band"], eth_df["middle_band"], eth_df["lower_band"] = ta.BBANDS(
            eth_df["close"], timeperiod=20, nbdevup=2, nbdevdn=2
        )

        btc_df["btc_rsi"] = ta.RSI(btc_df["btc_close"], timeperiod=14)

        eth_df["returns"] = eth_df["close"].pct_change()
        btc_df["btc_returns"] = btc_df["btc_close"].pct_change()

        merged_df = pd.merge(
            eth_df,
            btc_df.drop(columns=["datetime"]),
            left_index=True,
            right_index=True
        )

        merged_df["correlation"] = (
            merged_df["returns"]
            .rolling(window=24)
            .corr(merged_df["btc_returns"])
        )

        return merged_df

    def generate_signals(self, df):
        df = df.copy()
        df["trade_type"] = TradeType.HOLD.value
        df["Position"] = 0

        lookback = 72

        for i in range(lookback, len(df)):
            prev_position = df.loc[i-1, "Position"]

            correlation_threshold = df.loc[i-lookback:i, "correlation"].mean()
            current_correlation = df.loc[i, "correlation"]

            eth_oversold = df.loc[i, "rsi"] < 30
            eth_overbought = df.loc[i, "rsi"] > 70
            btc_oversold = df.loc[i, "btc_rsi"] < 30
            btc_overbought = df.loc[i, "btc_rsi"] > 70
            high_correlation = current_correlation > correlation_threshold

            long_condition = (
                (eth_oversold and btc_oversold and high_correlation) or
                (df.loc[i, "close"] < df.loc[i, "lower_band"])
            ) and prev_position == 0

            short_condition = (
                (eth_overbought and btc_overbought and high_correlation) or
                (df.loc[i, "close"] > df.loc[i, "upper_band"])
            ) and prev_position == 0

            exit_long_condition = (
                prev_position == 1 and
                ((df.loc[i, "close"] >= df.loc[i, "middle_band"]) or
                (df.loc[i, "rsi"] > 50))
            )

            exit_short_condition = (
                prev_position == -1 and
                ((df.loc[i, "close"] <= df.loc[i, "middle_band"]) or
                (df.loc[i, "rsi"] < 50))
            )

            if long_condition:
                df.loc[i, "Position"] = 1
            elif short_condition:
                df.loc[i, "Position"] = -1
            elif exit_long_condition or exit_short_condition:
                df.loc[i, "Position"] = 0
            else:
                df.loc[i, "Position"] = prev_position

        df["Position"] = df["Position"].fillna(0)

        for i in range(1, len(df)):
            if df.loc[i, "Position"] == 1 and df.loc[i-1, "Position"] != 1:
                df.loc[i, "trade_type"] = TradeType.LONG.value
            elif df.loc[i, "Position"] == -1 and df.loc[i-1, "Position"] != -1:
                df.loc[i, "trade_type"] = TradeType.SHORT.value
            elif df.loc[i, "Position"] == 1 and df.loc[i-1, "Position"] == -1:
                df.loc[i, "trade_type"] = TradeType.REVERSE_LONG.value
            elif df.loc[i, "Position"] == -1 and df.loc[i-1, "Position"] == 1:
                df.loc[i, "trade_type"] = TradeType.REVERSE_SHORT.value
            elif df.loc[i, "Position"] == 0 and df.loc[i-1, "Position"] != 0:
                df.loc[i, "trade_type"] = TradeType.CLOSE.value

        df.drop(columns=["Position"], inplace=True)

        return df

Example 7: Three symbols with the same timeframe#

import pandas as pd
import numpy as np
import talib as ta
from enum import Enum

class TradeType(Enum):
    LONG = "LONG"
    SHORT = "SHORT"
    REVERSE_LONG = "REVERSE_LONG"
    REVERSE_SHORT = "REVERSE_SHORT"
    CLOSE = "CLOSE"
    HOLD = "HOLD"

class Strategy:
    def run(self, data_ethusdt_1d: pd.DataFrame, data_btcusdt_1d: pd.DataFrame, data_solusdt_1d: pd.DataFrame) -> pd.DataFrame:
        data_ethusdt_1d = data_ethusdt_1d.copy()
        data_btcusdt_1d = data_btcusdt_1d.copy()
        data_solusdt_1d = data_solusdt_1d.copy()

        data_ethusdt_1d["datetime"] = pd.to_datetime(data_ethusdt_1d["datetime"])
        data_btcusdt_1d["datetime"] = pd.to_datetime(data_btcusdt_1d["datetime"])
        data_solusdt_1d["datetime"] = pd.to_datetime(data_solusdt_1d["datetime"])

        df = self.calculate_indicators(data_ethusdt_1d, data_btcusdt_1d, data_solusdt_1d)
        df = self.generate_signals(df)

        return df

    def calculate_indicators(self, eth_df, btc_df, sol_df):
        btc_columns = {
            'open': 'btc_open',
            'high': 'btc_high',
            'low': 'btc_low',
            'close': 'btc_close',
            'volume': 'btc_volume'
        }
        eth_columns = {
            'open': 'eth_open',
            'high': 'eth_high',
            'low': 'eth_low',
            'close': 'eth_close',
            'volume': 'eth_volume'
        }

        btc_df = btc_df.rename(columns=btc_columns)
        eth_df = eth_df.rename(columns=eth_columns)

        sol_df["rsi"] = ta.RSI(sol_df["close"], timeperiod=14)
        sol_df["upper_band"], sol_df["middle_band"], sol_df["lower_band"] = ta.BBANDS(
            sol_df["close"], timeperiod=20, nbdevup=2, nbdevdn=2
        )

        btc_df["btc_rsi"] = ta.RSI(btc_df["btc_close"], timeperiod=14)
        eth_df["eth_rsi"] = ta.RSI(eth_df["eth_close"], timeperiod=14)

        sol_df["returns"] = sol_df["close"].pct_change()
        btc_df["btc_returns"] = btc_df["btc_close"].pct_change()
        eth_df["eth_returns"] = eth_df["eth_close"].pct_change()

        merged_df = pd.merge(
            sol_df,
            btc_df.drop(columns=["datetime"]),
            left_index=True,
            right_index=True
        )

        merged_df = pd.merge(
            merged_df,
            eth_df.drop(columns=["datetime"]),
            left_index=True,
            right_index=True
        )

        merged_df["btc_correlation"] = (
            merged_df["returns"]
            .rolling(window=24)
            .corr(merged_df["btc_returns"])
        )

        merged_df["eth_correlation"] = (
            merged_df["returns"]
            .rolling(window=24)
            .corr(merged_df["eth_returns"])
        )

        return merged_df

    def generate_signals(self, df):
        df = df.copy()
        df["trade_type"] = TradeType.HOLD.value
        df["Position"] = 0

        lookback = 72

        for i in range(lookback, len(df)):
            prev_position = df.loc[i-1, "Position"]

            btc_correlation_threshold = df.loc[i-lookback:i, "btc_correlation"].mean()
            eth_correlation_threshold = df.loc[i-lookback:i, "eth_correlation"].mean()

            current_btc_correlation = df.loc[i, "btc_correlation"]
            current_eth_correlation = df.loc[i, "eth_correlation"]

            sol_oversold = df.loc[i, "rsi"] < 30
            sol_overbought = df.loc[i, "rsi"] > 70
            btc_oversold = df.loc[i, "btc_rsi"] < 30
            btc_overbought = df.loc[i, "btc_rsi"] > 70
            eth_oversold = df.loc[i, "eth_rsi"] < 30
            eth_overbought = df.loc[i, "eth_rsi"] > 70

            high_btc_correlation = current_btc_correlation > btc_correlation_threshold
            high_eth_correlation = current_eth_correlation > eth_correlation_threshold

            long_condition = (
                (sol_oversold and ((btc_oversold and high_btc_correlation) or (eth_oversold and high_eth_correlation))) or
                (df.loc[i, "close"] < df.loc[i, "lower_band"])
            ) and prev_position == 0

            short_condition = (
                (sol_overbought and ((btc_overbought and high_btc_correlation) or (eth_overbought and high_eth_correlation))) or
                (df.loc[i, "close"] > df.loc[i, "upper_band"])
            ) and prev_position == 0

            exit_long_condition = (
                prev_position == 1 and
                ((df.loc[i, "close"] >= df.loc[i, "middle_band"]) or
                (df.loc[i, "rsi"] > 50))
            )

            exit_short_condition = (
                prev_position == -1 and
                ((df.loc[i, "close"] <= df.loc[i, "middle_band"]) or
                (df.loc[i, "rsi"] < 50))
            )

            if long_condition:
                df.loc[i, "Position"] = 1
            elif short_condition:
                df.loc[i, "Position"] = -1
            elif exit_long_condition or exit_short_condition:
                df.loc[i, "Position"] = 0
            else:
                df.loc[i, "Position"] = prev_position

        df["Position"] = df["Position"].fillna(0)

        for i in range(1, len(df)):
            if df.loc[i, "Position"] == 1 and df.loc[i-1, "Position"] != 1:
                df.loc[i, "trade_type"] = TradeType.LONG.value
            elif df.loc[i, "Position"] == -1 and df.loc[i-1, "Position"] != -1:
                df.loc[i, "trade_type"] = TradeType.SHORT.value
            elif df.loc[i, "Position"] == 1 and df.loc[i-1, "Position"] == -1:
                df.loc[i, "trade_type"] = TradeType.REVERSE_LONG.value
            elif df.loc[i, "Position"] == -1 and df.loc[i-1, "Position"] == 1:
                df.loc[i, "trade_type"] = TradeType.REVERSE_SHORT.value
            elif df.loc[i, "Position"] == 0 and df.loc[i-1, "Position"] != 0:
                df.loc[i, "trade_type"] = TradeType.CLOSE.value

        df.drop(columns=["Position"], inplace=True)

        return df

Example 8: Merging of multiple timeframe#

import talib as ta
import pandas as pd
import numpy as np
from enum import Enum

class TradeType(Enum):
    LONG = "LONG"
    SHORT = "SHORT"
    REVERSE_LONG = "REVERSE_LONG"
    REVERSE_SHORT = "REVERSE_SHORT"
    CLOSE = "CLOSE"
    HOLD = "HOLD"

class Strategy:
    def run(self, data_btcusdt_1d: pd.DataFrame, data_btcusdt_1h: pd.DataFrame) -> pd.DataFrame:
        data_btcusdt_1h["datetime"] = pd.to_datetime(data_btcusdt_1h["datetime"])
        data_btcusdt_1d["datetime"] = pd.to_datetime(data_btcusdt_1d["datetime"])

        data_btcusdt_1h = self.calculate_indicators(data_btcusdt_1h)
        df = self.aggregate_hourly_to_daily(data_btcusdt_1d, data_btcusdt_1h)
        df.fillna(0, inplace=True)

        return self.generate_signals(df)

    def calculate_indicators(self, df):
        df = df.copy()
        df["upper_band"], df["middle_band"], df["lower_band"] = ta.BBANDS(df["close"], timeperiod=20)
        df["rsi"] = ta.RSI(df["close"], timeperiod=14)
        return df

    def aggregate_hourly_to_daily(self, df_daily, df_hourly):
        df_hourly = df_hourly.copy()
        df_hourly["date"] = pd.to_datetime(df_hourly["datetime"]).dt.date
        df_hourly["hour"] = pd.to_datetime(df_hourly["datetime"]).dt.hour

        daily_closing_price = df_hourly[["date", "close"]].groupby("date").last().reset_index()
        daily_closing_price["date"] = pd.to_datetime(daily_closing_price["date"]) + pd.Timedelta(days=1)
        daily_closing_price.rename(columns={"close": "close_hourly"}, inplace=True)

        daily_stats = df_hourly.groupby("date").agg({
            "rsi": ["min", "max"],
            "upper_band": "last",
            "lower_band": "last",
            "middle_band": "last"
        }).reset_index()

        daily_stats["date"] = pd.to_datetime(daily_stats["date"]) + pd.Timedelta(days=1)
        daily_stats.columns = ["date", "rsi_min", "rsi_max", "upper_band", "lower_band", "middle_band"]

        df_daily = df_daily.copy()
        df_daily["date"] = pd.to_datetime(df_daily["datetime"]).dt.date
        df_daily["date"] = pd.to_datetime(df_daily["date"])

        daily_closing_price["date"] = pd.to_datetime(daily_closing_price["date"])
        daily_stats["date"] = pd.to_datetime(daily_stats["date"])

        merged_df = df_daily.merge(daily_stats, on="date", how="left").merge(daily_closing_price, on="date", how="left")
        merged_df.drop(columns=["date"], inplace=True)
        return merged_df

    def generate_signals(self, df):
        df = df.copy()
        df["trade_type"] = TradeType.HOLD.value
        df["Position"] = 0

        for i in range(1, len(df)):
            prev_position = df.loc[i - 1, "Position"]
            prev_close = df.loc[i - 1, "close_hourly"]

            long_condition = (prev_close < df.loc[i - 1, "lower_band"]) & (df.loc[i - 1, "rsi_min"] < 30) & (prev_position == 0)
            short_condition = (prev_close > df.loc[i - 1, "upper_band"]) & (df.loc[i - 1, "rsi_max"] > 70) & (prev_position == 0)
            exit_long_condition = (prev_position == 1) & ((prev_close >= df.loc[i - 1, "middle_band"]) | (df.loc[i - 1, "rsi_max"] > 50))
            exit_short_condition = (prev_position == -1) & ((prev_close <= df.loc[i - 1, "middle_band"]) | (df.loc[i - 1, "rsi_min"] < 50))

            if long_condition:
                df.loc[i, "Position"] = 1
            elif short_condition:
                df.loc[i, "Position"] = -1
            elif exit_long_condition or exit_short_condition:
                df.loc[i, "Position"] = 0
            else:
                df.loc[i, "Position"] = prev_position

        df["Position"] = df["Position"].ffill().fillna(0)

        for i in range(1, len(df)):
            if df.loc[i, "Position"] == 1 and df.loc[i - 1, "Position"] != 1:
                df.loc[i, "trade_type"] = TradeType.LONG.value
            elif df.loc[i, "Position"] == -1 and df.loc[i - 1, "Position"] != -1:
                df.loc[i, "trade_type"] = TradeType.SHORT.value
            elif df.loc[i, "Position"] == 1 and df.loc[i - 1, "Position"] == -1:
                df.loc[i, "trade_type"] = TradeType.REVERSE_LONG.value
            elif df.loc[i, "Position"] == -1 and df.loc[i - 1, "Position"] == 1:
                df.loc[i, "trade_type"] = TradeType.REVERSE_SHORT.value
            elif df.loc[i, "Position"] == 0 and df.loc[i - 1, "Position"] != 0:
                df.loc[i, "trade_type"] = TradeType.CLOSE.value

        df.drop(columns=["Position"], inplace=True)
        return df

Example 9: Creating Orders using SDK#

This example demonstrates how to create a market/limit order using the UntradeSDK.

Example 5 - Creating Orders using SDK#
  1 from untrade.client import Client
  2
  3
  4 client = Client()
  5
  6
  7 # Function to create a market order without specifying a target stop loss
  8 def create_order_without_target_stop_loss():
  9     try:
 10         # Attempt to create a market order using the specified parameters
 11         response = client.create_order(
 12             symbol="BTCUSDT",  # Trading pair (Bitcoin to USDT)
 13             side="BUY",  # Order type: BUY (you can also use "SELL" for selling)
 14             type="MARKET",  # Order type: MARKET (executed immediately at the current market price)
 15             market="COIN-M",  # Market identifier (replace with the correct market if needed)
 16             quantity=100,  # Quantity of the asset to buy or sell
 17             leverage=10,  # Leverage level for the order (adjust as needed)
 18         )
 19         # Print a success message and display the order response details
 20         print("Order Created Successfully:")
 21         print(response, sort_dicts=False)
 22
 23     except Exception as e:
 24         # Print an error message if there's an exception during order creation
 25         print(f"Error creating order: {e}")
 26
 27
 28 # Call the function to execute the order creation
 29 create_order_without_target_stop_loss()
 30
 31
 32 # Function to create a market order with a target and stop loss
 33 def create_market_order_with_target_stop_loss():
 34     try:
 35         # Attempt to create a market order using the specified parameters
 36         response = client.create_order(
 37             symbol="BTCUSDT",  # Trading pair (Bitcoin to USDT)
 38             side="BUY",  # Order type: BUY (you can also use "SELL" for selling)
 39             type="MARKET",  # Order type: MARKET (executed immediately at the current market price)
 40             market="COIN-M",  # Market identifier (replace with the correct market if needed)
 41             quantity=100,  # Quantity of the asset to buy or sell
 42             leverage=10,  # Leverage level for the order (adjust as needed)
 43             target=45000,  # Target price for the market order
 44             stop_loss=35000,  # Stop-loss price for the market order
 45         )
 46         # Print a success message and display the order response details
 47         print("Order Created Successfully:")
 48         print(response, sort_dicts=False)
 49
 50     except Exception as e:
 51         # Print an error message if there's an exception during order creation
 52         print(f"Error creating order: {e}")
 53
 54
 55 # Call the function to execute the market order creation
 56 create_market_order_with_target_stop_loss()
 57
 58
 59 # Function to create a limit order with a target and stop loss
 60 def create_limit_order_with_target_stop_loss():
 61     try:
 62         # Attempt to create a limit order using the specified parameters
 63         response = client.create_order(
 64             symbol="BTCUSDT",  # Trading pair (Bitcoin to USDT)
 65             side="BUY",  # Order type: BUY (you can also use "SELL" for selling)
 66             type="LIMIT",  # Order type: LIMIT (executed at a specified price or better)
 67             market="COIN-M",  # Market identifier (replace with the correct market if needed)
 68             quantity=100,  # Quantity of the asset to buy or sell
 69             leverage=10,  # Leverage level for the order (adjust as needed)
 70             target=45000,  # Target price for the limit order
 71             stop_loss=35000,  # Stop-loss price for the limit order
 72             price=42000,  # Price at which the limit order will be executed or better
 73         )
 74         # Print a success message and display the order response details
 75         print("Order Created Successfully:")
 76         print(response, sort_dicts=False)
 77
 78     except Exception as e:
 79         # Print an error message if there's an exception during order creation
 80         print(f"Error creating order: {e}")
 81
 82
 83 # Call the function to execute the limit order creation
 84 create_limit_order_with_target_stop_loss()
 85
 86
 87 # Function to create a target order
 88 def create_target_order():
 89     try:
 90         # Attempt to create a take profit market order using the specified parameters
 91         response = client.create_target_order(
 92             symbol="BTCUSDT",  # Trading pair (Bitcoin to USDT)
 93             type="TAKE_PROFIT_MARKET",  # Order type: TAKE_PROFIT_MARKET
 94             market="COIN-M",  # Market identifier (replace with the correct market if needed)
 95             stop_price=45000,  # Stop price for the take profit order
 96             parent_order_id="68b195ec-150e-47e1-8e01-694b719acdd8",  # ID of the parent order
 97         )
 98         # Print a success message and display the order response details
 99         print("Target order created Successfully:")
100         print(response)
101     except Exception as e:
102         # Print an error message if there's an exception during order creation
103         print(f"An unexpected error occurred: {e}")
104
105
106 # Function to create a stop-loss order
107 def create_stoploss_order():
108     try:
109         # Attempt to create a stop market order using the specified parameters
110         response = client.create_stoploss_order(
111             symbol="BTCUSDT",  # Trading pair (Bitcoin to USDT)
112             type="STOP_MARKET",  # Order type: STOP_MARKET
113             market="COIN-M",  # Market identifier (replace with the correct market if needed)
114             stop_price=35000,  # Stop price for the stop-loss order
115             parent_order_id="68b195ec-150e-47e1-8e01-694b719acdd8",  # ID of the parent order
116         )
117         # Print a success message and display the order response details
118         print("Stop-loss order created successfully:")
119         print(response)
120     except Exception as e:
121         # Print an error message if there's an exception during order creation
122         print(f"An unexpected error occurred: {e}")
123
124
125 # Function to close an existing order
126 def close_existing_order():
127     try:
128         # Attempt to close the specified order using the specified parameters
129         response = client.close_order(
130             symbol="BTCUSDT",  # Trading pair (Bitcoin to USDT)
131             market="COIN-M",  # Market identifier (replace with the correct market if needed)
132             parent_order_id="68b195ec-150e-47e1-8e01-694b719acdd8",  # ID of the order to be closed
133         )
134         # Print a success message and display the order response details
135         print("Order closed successfully:")
136         print(response)
137     except Exception as e:
138         # Print an error message if there's an exception during order closing
139         print(f"An unexpected error occurred: {e}")
140
141
142 # Calling the functions
143 create_target_order()
144 create_stoploss_order()
145 close_existing_order()