Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/BNB/futures_long_profit_margin.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0.25
1 change: 1 addition & 0 deletions app/BNB/futures_short_profit_margin.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0.25
1 change: 1 addition & 0 deletions app/BNB/long_dca_signal.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0
1 change: 1 addition & 0 deletions app/BNB/short_dca_signal.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0
1 change: 1 addition & 0 deletions app/DOGE/futures_long_profit_margin.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0.25
1 change: 1 addition & 0 deletions app/DOGE/futures_short_profit_margin.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0.25
1 change: 1 addition & 0 deletions app/DOGE/long_dca_signal.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0
1 change: 1 addition & 0 deletions app/DOGE/short_dca_signal.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0
1 change: 1 addition & 0 deletions app/ETH/futures_long_profit_margin.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0.25
1 change: 1 addition & 0 deletions app/ETH/futures_short_profit_margin.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0.25
1 change: 1 addition & 0 deletions app/ETH/long_dca_signal.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0
1 change: 1 addition & 0 deletions app/ETH/short_dca_signal.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0
1 change: 1 addition & 0 deletions app/XRP/futures_long_profit_margin.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0.25
1 change: 1 addition & 0 deletions app/XRP/futures_short_profit_margin.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0.25
1 change: 1 addition & 0 deletions app/XRP/long_dca_signal.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0
1 change: 1 addition & 0 deletions app/XRP/short_dca_signal.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0
1 change: 1 addition & 0 deletions app/alerts_version.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
5/3/2022/9am
1 change: 1 addition & 0 deletions app/futures_long_onoff.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
OFF
1 change: 1 addition & 0 deletions app/futures_long_profit_margin.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0.25
1 change: 1 addition & 0 deletions app/futures_short_onoff.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
OFF
1 change: 1 addition & 0 deletions app/futures_short_profit_margin.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0.25
1 change: 1 addition & 0 deletions app/long_dca_signal.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0
7 changes: 6 additions & 1 deletion app/pt_hub.py
Original file line number Diff line number Diff line change
Expand Up @@ -6009,9 +6009,14 @@ def _check_exchange_status_worker(self):
time.sleep(5)
continue

primary_exchange = self.settings.get("primary_exchange", "robinhood")
primary_exchange = self.settings.get("primary_exchange", "")
region = self.settings.get("region", "us")

# Skip if no primary exchange is configured
if not primary_exchange:
time.sleep(10)
continue

# Check if primary exchange is available
available_exchanges = self._multi_exchange.get_available_exchanges()

Expand Down
6 changes: 3 additions & 3 deletions app/pt_multi_exchange.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,13 +90,13 @@ def create_default_config(self, user_region: str = "GLOBAL") -> TradingConfig:
if user_region.upper() in ["US", "USA"]:
# US users - Robinhood, Coinbase, global exchanges
exchanges = [
ExchangeConfig("robinhood", True, 1),
ExchangeConfig("coinbase", True, 2),
ExchangeConfig("robinhood", False, 1),
ExchangeConfig("coinbase", False, 2),
ExchangeConfig("kraken", False, 3),
ExchangeConfig("binance", False, 4),
ExchangeConfig("kucoin", False, 5),
]
primary = "robinhood"
primary = ""

elif user_region.upper() in ["EU", "EUROPE"]:
# EU users - Kraken, Bitstamp, global exchanges
Expand Down
92 changes: 62 additions & 30 deletions app/pt_thinker.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,25 @@

# Third-party imports
import requests
from kucoin.client import Market
from nacl.signing import SigningKey

# Local imports
from pt_credentials import get_credentials

# Initialize market client
market = Market(url="https://api.kucoin.com")
# Conditional imports for CI/CD compatibility
market = None
try:
from kucoin.client import Market

# Initialize market client
market = Market(url="https://api.kucoin.com")
except Exception as e:
# Skip kucoin in test environments or when package has issues
if os.environ.get("POWERTRADER_ENV") == "test":
print(f"β„Ή Skipping KuCoin client in test environment: {e}")
else:
print(f"⚠ KuCoin client unavailable: {e}")
market = None

# -----------------------------
# Robinhood market-data (current ASK), same source as rhcb.py trader:
Expand Down Expand Up @@ -525,6 +536,8 @@ def init_coin(sym: str):
history_list = []
while True:
try:
if market is None:
raise RuntimeError("KuCoin market client unavailable")
history = (
str(market.get_kline(coin, tf_choices[ind]))
.replace("]]", "], ")
Expand Down Expand Up @@ -557,12 +570,42 @@ def init_coin(sym: str):
states[sym] = st


# init all coins once (from GUI settings)
for _sym in CURRENT_COINS:
init_coin(_sym)
# Initialize only when run as main script, not during import
def main():
"""Main entry point for pt_thinker when run as a script."""
# init all coins once (from GUI settings)
for _sym in CURRENT_COINS:
init_coin(_sym)

# restore CWD to base after init
os.chdir(BASE_DIR)

# Main execution loop
try:
while True:
# Hot-reload coins from GUI settings while running
_sync_coins_from_settings()

for _sym in CURRENT_COINS:
step_coin(_sym)

# clear + re-print one combined screen (so you don't see old output above new)
os.system("cls" if os.name == "nt" else "clear")

for _sym in CURRENT_COINS:
print(display_cache.get(_sym, _sym + " (no data yet)"))
print("\n" + ("-" * 60) + "\n")

# small sleep so you don't peg CPU when running many coins
time.sleep(0.15)

except Exception:
PrintException()

# restore CWD to base after init
os.chdir(BASE_DIR)

if __name__ == "__main__":
# Only initialize when run directly, not when imported
main()


wallet_addr_list = []
Expand Down Expand Up @@ -624,6 +667,11 @@ def step_coin(sym: str):
# run inside the coin folder so all existing file reads/writes stay relative + isolated
os.chdir(coin_folder(sym))
coin = sym + "-USDT"

# Initialize state if it doesn't exist (for import testing scenarios)
if sym not in states:
states[sym] = new_coin_state()

st = states[sym]

# --- training freshness gate ---
Expand Down Expand Up @@ -697,6 +745,8 @@ def step_coin(sym: str):
history_list = []
while True:
try:
if market is None:
raise RuntimeError("KuCoin market client unavailable")
history = (
str(market.get_kline(coin, tf_choices[tf_choice_index]))
.replace("]]", "], ")
Expand Down Expand Up @@ -1005,6 +1055,8 @@ def _pad_to_len(lst, n, fill):
# update the_time snapshot (same as before)
while True:
try:
if market is None:
raise RuntimeError("KuCoin market client unavailable")
history = (
str(market.get_kline(coin, tf_choices[inder]))
.replace("]]", "], ")
Expand Down Expand Up @@ -1383,6 +1435,8 @@ def _pad_to_len(lst, n, fill):
while this_index_now < len(tf_update):
while True:
try:
if market is None:
raise RuntimeError("KuCoin market client unavailable")
history = (
str(market.get_kline(coin, tf_choices[this_index_now]))
.replace("]]", "], ")
Expand Down Expand Up @@ -1438,25 +1492,3 @@ def _pad_to_len(lst, n, fill):
st["training_issues"] = training_issues

states[sym] = st


try:
while True:
# Hot-reload coins from GUI settings while running
_sync_coins_from_settings()

for _sym in CURRENT_COINS:
step_coin(_sym)

# clear + re-print one combined screen (so you don't see old output above new)
os.system("cls" if os.name == "nt" else "clear")

for _sym in CURRENT_COINS:
print(display_cache.get(_sym, _sym + " (no data yet)"))
print("\n" + ("-" * 60) + "\n")

# small sleep so you don't peg CPU when running many coins
time.sleep(0.15)

except Exception:
PrintException()
66 changes: 44 additions & 22 deletions app/pt_trader.py
Original file line number Diff line number Diff line change
Expand Up @@ -371,30 +371,48 @@ def _refresh_paths_and_symbols():
base_paths = _build_base_paths(main_dir, crypto_symbols)


# API STUFF
API_KEY = ""
BASE64_PRIVATE_KEY = ""

try:
credentials = get_credentials()
if credentials:
API_KEY, BASE64_PRIVATE_KEY = credentials
else:
# API STUFF - Initialize as None, load when needed
API_KEY = None
BASE64_PRIVATE_KEY = None


def _load_credentials_if_needed():
"""Load credentials only when actually needed for trading operations"""
global API_KEY, BASE64_PRIVATE_KEY

if API_KEY is not None and BASE64_PRIVATE_KEY is not None:
return # Already loaded

# Check if we're in test/CI environment
if os.environ.get("POWERTRADER_ENV") == "test":
API_KEY = ""
BASE64_PRIVATE_KEY = ""
except Exception as e:
print(f"[PowerTrader] Error loading credentials: Credential system error")
API_KEY = ""
BASE64_PRIVATE_KEY = ""

if not API_KEY or not BASE64_PRIVATE_KEY:
print(
"\n[PowerTrader] Robinhood API credentials not found.\n"
"Open the GUI and go to Settings β†’ Robinhood API β†’ Setup / Update.\n"
"That wizard will generate your keypair, tell you where to paste the public key on Robinhood,\n"
"and will save encrypted credential files so this trader can authenticate securely.\n"
)
raise SystemExit(1)
return

try:
credentials = get_credentials()
if credentials:
API_KEY, BASE64_PRIVATE_KEY = credentials
else:
API_KEY = ""
BASE64_PRIVATE_KEY = ""
except Exception as e:
print(f"[PowerTrader] Error loading credentials: Credential system error")
API_KEY = ""
BASE64_PRIVATE_KEY = ""

if not API_KEY or not BASE64_PRIVATE_KEY:
print(
"\n[PowerTrader] Robinhood API credentials not found.\n"
"Open the GUI and go to Settings β†’ Robinhood API β†’ Setup / Update.\n"
"That wizard will generate your keypair, tell you where to paste the public key on Robinhood,\n"
"and will save encrypted credential files so this trader can authenticate securely.\n"
)
# Don't exit during import - let the calling code handle the error
return False

return True


# Initialize secure logging
logger = get_logger(__name__)
Expand All @@ -403,6 +421,10 @@ def _refresh_paths_and_symbols():

class CryptoAPITrading:
def __init__(self):
# Load credentials only when the class is actually instantiated
if not _load_credentials_if_needed():
raise RuntimeError("Robinhood API credentials not available")

# keep a copy of the folder map (same idea as trader.py)
self.path_map = dict(base_paths)

Expand Down
Loading
Loading