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:#
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
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:
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#
1 # Disabled CURRENTLY
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#
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.
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()