Compare commits
2 Commits
43d9c333ca
...
0db0405ea1
Author | SHA1 | Date | |
---|---|---|---|
0db0405ea1 | |||
f8fe2c089f |
25
.gitea/workflows/analyze.yaml
Normal file
25
.gitea/workflows/analyze.yaml
Normal file
@ -0,0 +1,25 @@
|
||||
on:
|
||||
# Trigger analysis when pushing to your main branches, and when creating a pull request.
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- master
|
||||
- develop
|
||||
- 'releases/**'
|
||||
run-name: ${{ gitea.actor }} is testing out Gitea Actions 🚀
|
||||
name: SonarQube Scan
|
||||
jobs:
|
||||
sonarqube:
|
||||
name: SonarQube Trigger
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checking out
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
# Disabling shallow clone is recommended for improving relevancy of reporting
|
||||
fetch-depth: 0
|
||||
- name: SonarQube Scan
|
||||
uses: kitabisa/sonarqube-action@v1.2.0
|
||||
with:
|
||||
host: https://sonar.ailaplacelab.com
|
||||
login: sqa_a3988da69583c0ae606dae085f14e6ec901675f7
|
107
app.py
107
app.py
@ -1,67 +1,72 @@
|
||||
from mexc_sdk import Spot
|
||||
from fastapi import FastAPI, Request
|
||||
from fastapi.responses import HTMLResponse
|
||||
from fastapi.templating import Jinja2Templates
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from pymongo import MongoClient
|
||||
from datetime import datetime
|
||||
import uvicorn
|
||||
from urllib.parse import quote_plus
|
||||
from dotenv import load_dotenv
|
||||
import logging
|
||||
import os
|
||||
import yaml
|
||||
|
||||
with open("coin.yaml", "r") as file:
|
||||
data = yaml.safe_load(file)
|
||||
interest_coins = data["interest"]
|
||||
|
||||
# Set up logging
|
||||
logging.basicConfig(
|
||||
level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s"
|
||||
)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Load the environment variables from the .env file
|
||||
load_dotenv()
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
class TradeBot(object):
|
||||
def __init__(self, api_key, api_secret):
|
||||
self.client = Spot(api_key=api_key, api_secret=api_secret)
|
||||
self.symbol = "BTCUSDT"
|
||||
|
||||
def change_symbol(self, new_symbol):
|
||||
self.symbol = new_symbol
|
||||
# Setup templates directory
|
||||
templates = Jinja2Templates(directory="templates")
|
||||
|
||||
def time(self):
|
||||
# Format is in dict
|
||||
return f"Server Time: {self.client.time()['serverTime']}"
|
||||
MONGO_URI = f'mongodb://{quote_plus(os.getenv("DB_USER"))}:{quote_plus(os.getenv("DB_PWD"))}@{os.getenv("DB_HOST")}:{os.getenv("DB_PORT")}'
|
||||
client = MongoClient(MONGO_URI)
|
||||
db = client[os.getenv("DB_NAME")] # Replace with your database name
|
||||
# collection = db["XRP"]
|
||||
|
||||
def get_market_price(self, symbol=None):
|
||||
"""Get current market price for a symbol"""
|
||||
try:
|
||||
symbol = symbol or self.symbol
|
||||
ticker = self.client.ticker_price(symbol)
|
||||
price = float(ticker["price"])
|
||||
logger.info(f"Current {symbol} price: {price}")
|
||||
return price
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting market price: {str(e)}")
|
||||
return None
|
||||
|
||||
def get_account_balance(self):
|
||||
"""Get account balance for all assets"""
|
||||
try:
|
||||
account_info = self.client.account_info()
|
||||
balances = account_info["balances"]
|
||||
@app.get("/", response_class=HTMLResponse)
|
||||
async def read_data(request: Request):
|
||||
return templates.TemplateResponse(
|
||||
request=request, name="index.html", context={"coins": interest_coins}
|
||||
)
|
||||
|
||||
logger.info("Account balances:")
|
||||
for balance in balances:
|
||||
if float(balance["free"]) > 0 or float(balance["locked"]) > 0:
|
||||
logger.info(
|
||||
f"{balance['asset']}: Free={balance['free']}, Locked={balance['locked']}"
|
||||
)
|
||||
|
||||
return balances
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting account balance: {str(e)}")
|
||||
return None
|
||||
@app.get("/coin/{coin}")
|
||||
async def view_coin_graph(request: Request, coin: str):
|
||||
if coin in interest_coins:
|
||||
return templates.TemplateResponse(
|
||||
request=request, name="coin.html", context={"coin": coin}
|
||||
)
|
||||
else:
|
||||
return {"coin": "not found"}
|
||||
|
||||
|
||||
@app.get("/data/{coin}")
|
||||
async def get_data(coin):
|
||||
collection = db[coin]
|
||||
# Get last 100 data points, sorted by time
|
||||
cursor = (
|
||||
collection.find({}, {"_id": 0, "timestamp": 1, "price": 1})
|
||||
.sort("timestamp", -1)
|
||||
.limit(100)
|
||||
)
|
||||
|
||||
data = list(cursor)
|
||||
|
||||
# Format the data for Chart.js
|
||||
times = []
|
||||
prices = []
|
||||
|
||||
for item in reversed(data): # Reverse to show oldest to newest
|
||||
times.append(str(item["timestamp"]))
|
||||
prices.append(float(item["price"]))
|
||||
|
||||
return {"times": times, "prices": prices}
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Access the variables
|
||||
api_key = os.getenv("API_KEY")
|
||||
api_secret = os.getenv("API_SECRET")
|
||||
tb = TradeBot(api_key, api_secret)
|
||||
print(tb.time())
|
||||
tb.get_market_price()
|
||||
tb.get_account_balance()
|
||||
uvicorn.run("app:app", host="0.0.0.0", port=8000, reload=True)
|
||||
|
9
coin.yaml
Normal file
9
coin.yaml
Normal file
@ -0,0 +1,9 @@
|
||||
interest :
|
||||
- BTC
|
||||
- XRP
|
||||
- DOGE
|
||||
- ETH
|
||||
- SOL
|
||||
|
||||
base:
|
||||
- USDT
|
@ -3,7 +3,7 @@ cattrs==24.1.2
|
||||
exceptiongroup==1.2.2
|
||||
importlib_resources==6.4.5
|
||||
jsii==1.106.0
|
||||
mexc-sdk @ file:///mnt/c/Users/dongho/Desktop/mexc/mexc-api-sdk/dist/python/mexc_sdk-1.0.0-py3-none-any.whl
|
||||
mexc-sdk @ ./package/mexc_sdk-1.0.0-py3-none-any.whl
|
||||
publication==0.0.3
|
||||
python-dateutil==2.9.0.post0
|
||||
python-dotenv==1.0.1
|
||||
|
153
server.py
Normal file
153
server.py
Normal file
@ -0,0 +1,153 @@
|
||||
from mexc_sdk import Spot
|
||||
from dotenv import load_dotenv
|
||||
import yaml
|
||||
from pymongo import MongoClient
|
||||
from pymongo.errors import ConnectionFailure, OperationFailure
|
||||
import pytz
|
||||
|
||||
import logging
|
||||
import os
|
||||
from datetime import datetime
|
||||
import time
|
||||
from urllib.parse import quote_plus
|
||||
|
||||
|
||||
# Set up logging
|
||||
logging.basicConfig(
|
||||
level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s"
|
||||
)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Load the environment variables from the .env file
|
||||
load_dotenv()
|
||||
|
||||
|
||||
class MongoDBConn(object):
|
||||
def __init__(self):
|
||||
"""Initialize MongoDB connection parameters"""
|
||||
self.username = quote_plus(os.getenv("DB_USER"))
|
||||
self.password = quote_plus(os.getenv("DB_PWD"))
|
||||
self.host = os.getenv("DB_HOST")
|
||||
self.port = os.getenv("DB_PORT")
|
||||
self.db_name = os.getenv("DB_NAME")
|
||||
self.client = None
|
||||
self.db = None
|
||||
|
||||
def connect(self):
|
||||
"""Establish connection to MongoDB"""
|
||||
try:
|
||||
# Create connection URI
|
||||
uri = f"mongodb://{self.username}:{self.password}@{self.host}:{self.port}"
|
||||
|
||||
# Connect to MongoDB
|
||||
self.client = MongoClient(uri)
|
||||
|
||||
# Test the connection
|
||||
self.client.admin.command("ping")
|
||||
|
||||
# Get database reference
|
||||
self.db = self.client[self.db_name]
|
||||
|
||||
logger.info("Successfully connected to MongoDB")
|
||||
return True
|
||||
|
||||
except ConnectionFailure as e:
|
||||
logger.error(f"Could not connect to MongoDB: {e}")
|
||||
return False
|
||||
except OperationFailure as e:
|
||||
logger.error(f"Authentication failed: {e}")
|
||||
return False
|
||||
except Exception as e:
|
||||
logger.error(f"An error occurred: {e}")
|
||||
return False
|
||||
|
||||
def close(self):
|
||||
"""Close the MongoDB connection"""
|
||||
if self.client:
|
||||
self.client.close()
|
||||
logger.info("MongoDB connection closed")
|
||||
|
||||
def insert_coin_price(self, collection_name, document):
|
||||
"""Insert a single document into a collection"""
|
||||
try:
|
||||
collection = self.db[collection_name]
|
||||
result = collection.insert_one(document)
|
||||
logger.info(f"Document inserted with ID: {result.inserted_id}")
|
||||
return result.inserted_id
|
||||
except Exception as e:
|
||||
logger.error(f"Error inserting document: {e}")
|
||||
return None
|
||||
|
||||
|
||||
class TradeBot(object):
|
||||
def __init__(self, api_key, api_secret, mdb: MongoDBConn):
|
||||
self.client = Spot(api_key=api_key, api_secret=api_secret)
|
||||
self._load_coin()
|
||||
self.timezone = pytz.timezone("Asia/Seoul")
|
||||
self.mdb = mdb
|
||||
|
||||
def ttime(self):
|
||||
# Format is in dict
|
||||
return f"Server Time: {self.client.time()['serverTime']}"
|
||||
|
||||
def _get_current_time(self):
|
||||
return datetime.now(self.timezone)
|
||||
|
||||
def _get_market_price(self, symbol=None):
|
||||
"""Get current market price for a symbol"""
|
||||
try:
|
||||
symbol = symbol or self.symbol
|
||||
ticker = self.client.ticker_price(symbol)
|
||||
price = float(ticker["price"])
|
||||
logger.info(f"Current {symbol} price: {price}")
|
||||
return price
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting market price: {str(e)}")
|
||||
return None
|
||||
|
||||
def get_all_interset_market_price(self):
|
||||
for coin in self.interest_coins:
|
||||
data = {}
|
||||
data["price"] = self._get_market_price(symbol=coin + self.base)
|
||||
data["timestamp"] = self._get_current_time()
|
||||
self.mdb.insert_coin_price(collection_name=coin, document=data)
|
||||
|
||||
def get_account_balance(self):
|
||||
"""Get account balance for all assets"""
|
||||
try:
|
||||
account_info = self.client.account_info()
|
||||
balances = account_info["balances"]
|
||||
|
||||
logger.info("Account balances:")
|
||||
for balance in balances:
|
||||
if float(balance["free"]) > 0 or float(balance["locked"]) > 0:
|
||||
logger.info(
|
||||
f"{balance['asset']}: Free={balance['free']}, Locked={balance['locked']}"
|
||||
)
|
||||
|
||||
return balances
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting account balance: {str(e)}")
|
||||
return None
|
||||
|
||||
def _load_coin(self):
|
||||
data = None
|
||||
with open("coin.yaml", "r") as file:
|
||||
data = yaml.safe_load(file)
|
||||
self.interest_coins = data["interest"]
|
||||
self.base = data["base"][0]
|
||||
|
||||
|
||||
def main(tb: TradeBot):
|
||||
while True:
|
||||
tb.get_all_interset_market_price()
|
||||
time.sleep(5)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
mdb = MongoDBConn()
|
||||
mdb.connect()
|
||||
api_key = os.getenv("API_KEY")
|
||||
api_secret = os.getenv("API_SECRET")
|
||||
tb = TradeBot(api_key, api_secret, mdb)
|
||||
main(tb)
|
123
templates/coin.html
Normal file
123
templates/coin.html
Normal file
@ -0,0 +1,123 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>XRP Price Live Chart</title>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.7.0/chart.min.js"></script>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 20px;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
background-color: white;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||
}
|
||||
h1 {
|
||||
color: #333;
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.chart-container {
|
||||
position: relative;
|
||||
height: 60vh;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>XRP Price Live Chart</h1>
|
||||
<div class="chart-container">
|
||||
<canvas id="priceChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let chart;
|
||||
|
||||
async function fetchData() {
|
||||
try {
|
||||
const response = await fetch('/data/{{coin}}');
|
||||
const data = await response.json();
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error('Error fetching data:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async function updateChart() {
|
||||
const data = await fetchData();
|
||||
if (!data) return;
|
||||
|
||||
if (chart) {
|
||||
chart.data.labels = data.times;
|
||||
chart.data.datasets[0].data = data.prices;
|
||||
chart.update();
|
||||
} else {
|
||||
const ctx = document.getElementById('priceChart').getContext('2d');
|
||||
chart = new Chart(ctx, {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: data.times,
|
||||
datasets: [{
|
||||
label: 'XRP Price (USD)',
|
||||
data: data.prices,
|
||||
borderColor: 'rgb(75, 192, 192)',
|
||||
borderWidth: 2,
|
||||
fill: true,
|
||||
backgroundColor: 'rgba(75, 192, 192, 0.1)',
|
||||
tension: 0.1,
|
||||
pointRadius: 1,
|
||||
pointHoverRadius: 5
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: {
|
||||
position: 'top',
|
||||
},
|
||||
title: {
|
||||
display: true,
|
||||
text: 'XRP Price History'
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: false,
|
||||
ticks: {
|
||||
callback: function(value) {
|
||||
return '$' + value.toFixed(4);
|
||||
}
|
||||
}
|
||||
},
|
||||
x: {
|
||||
ticks: {
|
||||
maxTicksLimit: 10,
|
||||
maxRotation: 45,
|
||||
minRotation: 45
|
||||
}
|
||||
}
|
||||
},
|
||||
interaction: {
|
||||
intersect: false,
|
||||
mode: 'index'
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Update every 5 seconds
|
||||
updateChart();
|
||||
setInterval(updateChart, 5000);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
41
templates/index.html
Normal file
41
templates/index.html
Normal file
@ -0,0 +1,41 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Billionaire yes</title>
|
||||
<style>
|
||||
/* Add your CSS here */
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: Arial, sans-serif;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Add your content here -->
|
||||
<header>
|
||||
<h1>To Become Billionaire</h1>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<section>
|
||||
<h2>Interested Coints Live Chart</h2>
|
||||
<ul>
|
||||
{% for coin in coins %}
|
||||
<li><a href="/coin/{{coin}}">{{coin}}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<p>© 2024 Dongho Kim</p>
|
||||
</footer>
|
||||
|
||||
<script>
|
||||
// Add your JavaScript here
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
Reference in New Issue
Block a user