Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import random | |
| from PIL import Image | |
| import numpy as np | |
| import os | |
| import matplotlib.pyplot as plt | |
| from io import BytesIO | |
| import base64 | |
| # Candlestick patterns data | |
| candlestick_patterns = [ | |
| { | |
| "name": "Doji", | |
| "image": "doji.png", | |
| "signal": "neutral", | |
| "explanation": "A Doji forms when the opening and closing prices are virtually equal. It represents indecision in the market and can signal a potential reversal or continuation depending on previous price action." | |
| }, | |
| { | |
| "name": "Hammer", | |
| "image": "hammer.png", | |
| "signal": "buy", | |
| "explanation": "The Hammer is a bullish reversal pattern that forms during a downtrend. It has a small body at the top with a long lower shadow (at least twice the size of the body). This indicates that sellers drove prices down during the session but buyers were able to push the price back up by closing." | |
| }, | |
| { | |
| "name": "Shooting Star", | |
| "image": "shooting_star.png", | |
| "signal": "sell", | |
| "explanation": "The Shooting Star is a bearish reversal pattern that forms during an uptrend. It has a small body at the bottom with a long upper shadow. This signals that buyers pushed prices up during the session, but sellers took control and drove the price back down." | |
| }, | |
| { | |
| "name": "Bullish Engulfing", | |
| "image": "bullish_engulfing.png", | |
| "signal": "buy", | |
| "explanation": "A Bullish Engulfing pattern forms when a small bearish candle is followed by a larger bullish candle that completely 'engulfs' the previous day's body. This indicates strong buying pressure and often signals a bullish reversal." | |
| }, | |
| { | |
| "name": "Bearish Engulfing", | |
| "image": "bearish_engulfing.png", | |
| "signal": "sell", | |
| "explanation": "A Bearish Engulfing pattern forms when a small bullish candle is followed by a larger bearish candle that completely 'engulfs' the previous day's body. This indicates strong selling pressure and often signals a bearish reversal." | |
| }, | |
| { | |
| "name": "Morning Star", | |
| "image": "morning_star.png", | |
| "signal": "buy", | |
| "explanation": "The Morning Star is a bullish reversal pattern consisting of three candles: a large bearish candle, followed by a small-bodied candle (star) that gaps down, followed by a large bullish candle that closes above the midpoint of the first candle. This shows a shift from selling to buying pressure." | |
| }, | |
| { | |
| "name": "Evening Star", | |
| "image": "evening_star.png", | |
| "signal": "sell", | |
| "explanation": "The Evening Star is a bearish reversal pattern consisting of three candles: a large bullish candle, followed by a small-bodied candle (star) that gaps up, followed by a large bearish candle that closes below the midpoint of the first candle. This shows a shift from buying to selling pressure." | |
| }, | |
| { | |
| "name": "Three White Soldiers", | |
| "image": "three_white_soldiers.png", | |
| "signal": "buy", | |
| "explanation": "The Three White Soldiers pattern consists of three consecutive bullish candles, each opening within the previous candle's body and closing above the previous candle's high. It signals strong buying pressure and often indicates a bullish reversal." | |
| } | |
| ] | |
| # Generate placeholder images (since we don't have actual images) | |
| def generate_candlestick_image(pattern_name): | |
| fig, ax = plt.subplots(figsize=(6, 3)) | |
| # Different patterns have different appearances | |
| if pattern_name == "Doji": | |
| # Create a doji candlestick (open/close at same level) | |
| plt.plot([1], [50], 'ro', markersize=10) | |
| plt.plot([1], [30], '_', markersize=20, color='black') | |
| plt.plot([1], [70], '_', markersize=20, color='black') | |
| plt.plot([1, 1], [30, 70], 'k-') | |
| elif pattern_name == "Hammer": | |
| # Create a hammer candlestick | |
| plt.bar([1], [5], bottom=[45], width=0.4, color='green') | |
| plt.plot([1.2], [30], '_', markersize=20, color='black') | |
| plt.plot([1.2, 1.2], [30, 50], 'k-') | |
| elif pattern_name == "Shooting Star": | |
| # Create a shooting star candlestick | |
| plt.bar([1], [5], bottom=[45], width=0.4, color='red') | |
| plt.plot([1.2], [70], '_', markersize=20, color='black') | |
| plt.plot([1.2, 1.2], [50, 70], 'k-') | |
| elif pattern_name == "Bullish Engulfing": | |
| # First candle (small bearish) | |
| plt.bar([0.7], [5], bottom=[50], width=0.4, color='red') | |
| plt.plot([0.7, 0.7], [45, 55], 'k-') | |
| # Second candle (large bullish engulfing) | |
| plt.bar([1.3], [15], bottom=[40], width=0.4, color='green') | |
| plt.plot([1.3, 1.3], [35, 60], 'k-') | |
| elif pattern_name == "Bearish Engulfing": | |
| # First candle (small bullish) | |
| plt.bar([0.7], [5], bottom=[50], width=0.4, color='green') | |
| plt.plot([0.7, 0.7], [45, 55], 'k-') | |
| # Second candle (large bearish engulfing) | |
| plt.bar([1.3], [15], bottom=[40], width=0.4, color='red') | |
| plt.plot([1.3, 1.3], [35, 60], 'k-') | |
| elif pattern_name == "Morning Star": | |
| # First candle (large bearish) | |
| plt.bar([0.5], [10], bottom=[50], width=0.3, color='red') | |
| plt.plot([0.5, 0.5], [48, 62], 'k-') | |
| # Second candle (small doji) | |
| plt.bar([1], [2], bottom=[45], width=0.3, color='gray') | |
| plt.plot([1, 1], [44, 48], 'k-') | |
| # Third candle (large bullish) | |
| plt.bar([1.5], [10], bottom=[43], width=0.3, color='green') | |
| plt.plot([1.5, 1.5], [40, 55], 'k-') | |
| elif pattern_name == "Evening Star": | |
| # First candle (large bullish) | |
| plt.bar([0.5], [10], bottom=[45], width=0.3, color='green') | |
| plt.plot([0.5, 0.5], [43, 57], 'k-') | |
| # Second candle (small doji) | |
| plt.bar([1], [2], bottom=[58], width=0.3, color='gray') | |
| plt.plot([1, 1], [57, 61], 'k-') | |
| # Third candle (large bearish) | |
| plt.bar([1.5], [10], bottom=[45], width=0.3, color='red') | |
| plt.plot([1.5, 1.5], [43, 57], 'k-') | |
| elif pattern_name == "Three White Soldiers": | |
| # Three consecutive bullish candles | |
| plt.bar([0.5], [7], bottom=[40], width=0.3, color='green') | |
| plt.plot([0.5, 0.5], [38, 50], 'k-') | |
| plt.bar([1], [7], bottom=[47], width=0.3, color='green') | |
| plt.plot([1, 1], [45, 57], 'k-') | |
| plt.bar([1.5], [7], bottom=[54], width=0.3, color='green') | |
| plt.plot([1.5, 1.5], [52, 64], 'k-') | |
| plt.title(f"{pattern_name} Pattern") | |
| plt.xlim(0, 2) | |
| plt.ylim(20, 80) | |
| plt.axis('off') | |
| plt.tight_layout() | |
| # Convert plot to image | |
| buf = BytesIO() | |
| plt.savefig(buf, format='png') | |
| plt.close(fig) | |
| buf.seek(0) | |
| img = Image.open(buf) | |
| return img | |
| # Function to generate quiz questions | |
| def generate_questions(num_questions=10): | |
| questions = [] | |
| for i in range(num_questions): | |
| pattern_index = i % len(candlestick_patterns) | |
| current_pattern = candlestick_patterns[pattern_index] | |
| # Create options (all patterns except the correct one) | |
| other_options = [p["name"] for p in candlestick_patterns if p["name"] != current_pattern["name"]] | |
| # Randomly select 3 incorrect options | |
| random.shuffle(other_options) | |
| shuffled_options = other_options[:3] | |
| # Add the correct answer and shuffle again | |
| shuffled_options.append(current_pattern["name"]) | |
| random.shuffle(shuffled_options) | |
| questions.append({ | |
| "pattern": current_pattern, | |
| "options": shuffled_options, | |
| "correct_answer": current_pattern["name"] | |
| }) | |
| return questions | |
| # Quiz state (will be managed in session state) | |
| class QuizState: | |
| def __init__(self): | |
| self.reset() | |
| def reset(self): | |
| self.questions = generate_questions(10) | |
| self.current_question_index = 0 | |
| self.score = 0 | |
| self.answered = False | |
| self.selected_option = None | |
| self.is_complete = False | |
| # Initialize quiz state | |
| quiz_state = QuizState() | |
| def get_current_question(): | |
| return quiz_state.questions[quiz_state.current_question_index] | |
| def create_interface(): | |
| with gr.Blocks(css=""" | |
| .container { max-width: 800px; margin: 0 auto; } | |
| .header { background-color: #4338ca; color: white; padding: 20px; border-radius: 10px; text-align: center; margin-bottom: 20px; } | |
| .quiz-container { background-color: white; border-radius: 8px; padding: 20px; margin-bottom: 20px; } | |
| .option { background-color: #f3f4f6; border: 2px solid #e2e8f0; border-radius: 8px; padding: 15px; margin-bottom: 10px; cursor: pointer; } | |
| .option:hover { background-color: #e2e8f0; } | |
| .correct { border-color: #10b981; background-color: rgba(16, 185, 129, 0.1); } | |
| .incorrect { border-color: #ef4444; background-color: rgba(239, 68, 68, 0.1); } | |
| .selected { border-color: #4338ca; background-color: rgba(67, 56, 202, 0.1); } | |
| .explanation { background-color: #f1f5f9; border-left: 4px solid #4338ca; padding: 15px; border-radius: 0 8px 8px 0; margin-bottom: 20px; } | |
| .signal { display: inline-block; padding: 6px 12px; border-radius: 4px; color: white; font-weight: bold; margin-bottom: 10px; } | |
| .buy { background-color: #10b981; } | |
| .sell { background-color: #ef4444; } | |
| .neutral { background-color: #6b7280; } | |
| .cash-reward { background-color: #fbbf24; color: #1f2937; padding: 10px 20px; border-radius: 8px; font-size: 24px; font-weight: bold; display: inline-block; margin: 20px 0; } | |
| """) as demo: | |
| gr.HTML(""" | |
| <div class="header"> | |
| <h1>Candlestick Pattern Quiz Game</h1> | |
| <p>Test your knowledge of candlestick patterns and earn reward points!</p> | |
| </div> | |
| """) | |
| with gr.Group(visible=True) as quiz_container: | |
| progress = gr.HTML(value="Question: 1/10 | Score: 0") | |
| pattern_image = gr.Image(label="Candlestick Pattern", type="pil") | |
| question_text = gr.Markdown("## What candlestick pattern is shown above?") | |
| with gr.Row(): | |
| with gr.Column(): | |
| option1 = gr.Button(value="Option 1") | |
| option2 = gr.Button(value="Option 2") | |
| with gr.Column(): | |
| option3 = gr.Button(value="Option 3") | |
| option4 = gr.Button(value="Option 4") | |
| explanation_box = gr.Markdown(visible=False) | |
| next_button = gr.Button("Next Question", interactive=False) | |
| with gr.Group(visible=False) as results_container: | |
| gr.Markdown("## Quiz Complete!") | |
| final_score = gr.Markdown("Your final score: 0/10") | |
| cash_reward = gr.HTML('<div class="cash-reward">Reward: $0.00</div>') | |
| gr.Markdown("You've earned points that can be converted to cash rewards!") | |
| restart_button = gr.Button("Try Again") | |
| # Functions to update the interface | |
| def update_progress(): | |
| return f"Question: {quiz_state.current_question_index + 1}/10 | Score: {quiz_state.score}" | |
| def load_question(): | |
| if quiz_state.is_complete: | |
| return { | |
| quiz_container: gr.update(visible=False), | |
| results_container: gr.update(visible=True), | |
| final_score: f"Your final score: {quiz_state.score}/10", | |
| cash_reward: f'<div class="cash-reward">Reward: ${(quiz_state.score * 0.5):.2f}</div>' | |
| } | |
| question = get_current_question() | |
| img = generate_candlestick_image(question["pattern"]["name"]) | |
| return { | |
| progress: update_progress(), | |
| pattern_image: img, | |
| option1: question["options"][0], | |
| option2: question["options"][1], | |
| option3: question["options"][2], | |
| option4: question["options"][3], | |
| explanation_box: gr.update(value="", visible=False), | |
| next_button: gr.update(interactive=False) | |
| } | |
| def select_option(option_text, btn_index): | |
| if quiz_state.answered: | |
| return {} | |
| quiz_state.selected_option = option_text | |
| quiz_state.answered = True | |
| question = get_current_question() | |
| correct = option_text == question["correct_answer"] | |
| if correct: | |
| quiz_state.score += 1 | |
| # Prepare explanation | |
| pattern = question["pattern"] | |
| signal_class = "buy" if pattern["signal"] == "buy" else "sell" if pattern["signal"] == "sell" else "neutral" | |
| explanation_html = "" | |
| if correct: | |
| explanation_html += "### Correct! π\n" | |
| else: | |
| explanation_html += f"### Incorrect\nThe correct answer is: **{question['correct_answer']}**\n" | |
| explanation_html += f"<div class='signal {signal_class}'>{pattern['signal'].upper()}</div>\n\n" | |
| explanation_html += pattern["explanation"] | |
| # Update button styles | |
| button_updates = {} | |
| for i, opt_text in enumerate([option1.value, option2.value, option3.value, option4.value]): | |
| btn_key = [option1, option2, option3, option4][i] | |
| if opt_text == question["correct_answer"]: | |
| button_updates[btn_key] = gr.update(variant="primary", value=f"β {opt_text}") | |
| elif opt_text == option_text and not correct: | |
| button_updates[btn_key] = gr.update(variant="stop", value=f"β {opt_text}") | |
| return { | |
| **button_updates, | |
| explanation_box: gr.update(value=explanation_html, visible=True), | |
| next_button: gr.update(interactive=True), | |
| progress: update_progress() | |
| } | |
| def next_question(): | |
| quiz_state.current_question_index += 1 | |
| quiz_state.answered = False | |
| quiz_state.selected_option = None | |
| if quiz_state.current_question_index >= len(quiz_state.questions): | |
| quiz_state.is_complete = True | |
| return load_question() | |
| def restart_quiz(): | |
| quiz_state.reset() | |
| return { | |
| quiz_container: gr.update(visible=True), | |
| results_container: gr.update(visible=False), | |
| **load_question() | |
| } | |
| # Set up event handlers | |
| option1.click(lambda: select_option(option1.value, 0)) | |
| option2.click(lambda: select_option(option2.value, 1)) | |
| option3.click(lambda: select_option(option3.value, 2)) | |
| option4.click(lambda: select_option(option4.value, 3)) | |
| next_button.click(next_question) | |
| restart_button.click(restart_quiz) | |
| # Initialize the quiz | |
| demo.load(load_question) | |
| return demo | |
| # Create and launch the interface | |
| demo = create_interface() | |
| # For Hugging Face Spaces | |
| if __name__ == "__main__": | |
| demo.launch() |