diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..d4a4aa6 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,66 @@ +name: Build + +on: + workflow_dispatch: + push: + branches: ["main"] + tags: ["v*.*.*"] + pull_request: + branches: ["main"] + +permissions: + contents: write + +jobs: + build: + runs-on: macos-latest + steps: + - name: 📥 Checkout repository + uses: actions/checkout@v3 + + - name: 🧰 Download Typst + uses: robinraju/release-downloader@v1.7 + with: + repository: typst/typst + tag: v0.10.0 + fileName: typst-x86_64-apple-darwin.tar.xz + + - name: 🧰 Extract Typst + shell: bash + run: | + sudo mkdir /usr/local/typst + sudo tar -xzf typst-x86_64-apple-darwin.tar.xz -C /usr/local/typst/ + rm -f typst-x86_64-apple-darwin.tar.xz + echo "/usr/local/typst/typst-x86_64-apple-darwin" >> $GITHUB_PATH + + - run: "echo $GITHUB_SHA > ./version.txt" + + - name: 📘 Compile + shell: bash + run: ./build.sh + + - name: 📦 Upload artifacts + uses: actions/upload-artifact@v3 + with: + name: oracle_spec + path: ./*.pdf + + - name: 🚀 Release + uses: softprops/action-gh-release@v1 + if: github.ref_type == 'tag' + with: + name: ${{ github.ref_name }} + tag_name: ${{ github.ref_name }} + files: "./*.pdf" + + - run: "mkdir output && mv spec.pdf output/spec.pdf" + + - name: Publish + uses: ryand56/r2-upload-action@latest + with: + r2-account-id: ${{ secrets.R2_ACCOUNT_ID }} + r2-access-key-id: ${{ secrets.R2_ACCESS_KEY_ID }} + r2-secret-access-key: ${{ secrets.R2_SECRET_ACCESS_KEY }} + r2-bucket: ${{ secrets.R2_BUCKET }} + source-dir: output + destination-dir: ./ \ No newline at end of file diff --git a/img/example.png b/img/example.png new file mode 100644 index 0000000..1955704 Binary files /dev/null and b/img/example.png differ diff --git a/img/logo-black.png b/img/logo-black.png new file mode 100644 index 0000000..df0f028 Binary files /dev/null and b/img/logo-black.png differ diff --git a/oracles.py b/oracles.py new file mode 100644 index 0000000..bc062c8 --- /dev/null +++ b/oracles.py @@ -0,0 +1,61 @@ +import matplotlib.pyplot as plt +import numpy as np + +def generate_price_data(initial_price, num_days, volatility): + """Generate price data using random walk.""" + prices = [initial_price] + for _ in range(num_days - 1): + change = np.random.normal(0, volatility) + new_price = max(0, prices[-1] + change) # Ensure price doesn't go negative + prices.append(new_price) + return prices + +def calculate_modified_ema(prices, period=20): + """Calculate Modified Exponential Moving Average.""" + ema = [prices[0]] + multiplier = 2 / (period + 1) + for price in prices[1:]: + if price < ema[-1]: + ema.append(price) # Immediately adjust to price decrease + else: + ema.append((price - ema[-1]) * multiplier + ema[-1]) # Use EMA for price increase + return ema + +def calculate_simple_ema(prices, period=20): + """ + Calculate Simple Exponential Moving Average with immediate price decrease reflection. + + This is the preferred model for our analysis. It has the following characteristics: + 1. Immediately adjusts to price decreases, providing quick responsiveness to market downturns. + 2. Uses standard EMA calculation for price increases, allowing for a smoother trend during upward movements. + + This combination allows for quick reaction to potential losses while maintaining trend stability during growth periods. + """ + ema = [prices[0]] + multiplier = 2 / (period + 1) + for price in prices[1:]: + if price < ema[-1]: + ema.append(price) # Immediately adjust to price decrease + else: + ema.append((price - ema[-1]) * multiplier + ema[-1]) # Use EMA for price increase + return ema + +def plot_asset_price(asset_name, initial_price=100, num_days=365, volatility=1): + """Plot the price of an asset over time.""" + days = range(num_days) + prices = generate_price_data(initial_price, num_days, volatility) + simple_ema = calculate_simple_ema(prices) + + plt.figure(figsize=(10, 6)) + plt.plot(days, prices, label='Price') + plt.plot(days, simple_ema, label='Simple EMA (Preferred Model)', color='red') + plt.title(f"Price of {asset_name} Over Time") + plt.xlabel("Days") + plt.ylabel("Price") + plt.grid(True) + plt.legend() + plt.savefig('img/example.png') + plt.close() + +# Example usage +plot_asset_price("XYZ", initial_price=100, num_days=365, volatility=1) diff --git a/spec.typ b/spec.typ new file mode 100644 index 0000000..5aa778a --- /dev/null +++ b/spec.typ @@ -0,0 +1,84 @@ +#let special_version = read("./version.txt") +#align(center, [ + #v(2em) + #[#set par(justify: true) + #image("img/logo-black.png", width: 120pt)] + #set text(size:18pt) + + Oracle Specifications + #align(center, text(12pt, weight: 400)[ + v. #special_version + ]) + + #datetime.today().display("[day] [month repr:long], [year]") +]) +#v(2em) += Simple EMA Oracle Specification + +== Overview +This specification outlines the Simple Exponential Moving Average (EMA) oracle algorithm, which is designed to provide a responsive and adaptive measure of asset price trends. The algorithm is particularly suited for financial applications where quick reactions to market downturns are crucial while maintaining stability during upward trends. + +== Algorithm Description +The Simple EMA algorithm uses a single piece of previous state to add a drag for any price increases while immediately reflecting price decreases. This approach combines the benefits of quick responsiveness to potential losses with the stability of trend following during growth periods. + +== Key Features +1. Immediate reflection of price decreases +2. Smoothed response to price increases using EMA calculation +3. Single state variable for efficient implementation + +== Implementation Details + +=== Inputs +- `prices`: A list of asset prices over time +- `period`: The number of days for the EMA calculation (default: 20) + +=== Output +- A list of EMA values corresponding to the input prices + +== Visual Representation + +The following graph illustrates the behavior of the Simple EMA algorithm compared to the actual price of an asset over time: + +#figure( + image("img/example.png", width: 80%), + caption: [ + Comparison of Asset Price and Simple EMA over time. + The red line represents the Simple EMA, which quickly adapts to price decreases while smoothing price increases. + ] +) + +This visual representation demonstrates how the Simple EMA (red line) closely follows the asset price (blue line) during downward movements, while providing a smoother trend during upward movements. This behavior aligns with the algorithm's design to be responsive to potential losses while maintaining stability during growth periods. + + +=== Pseudocode + +#figure([ + #set text(font: "New Computer Modern Mono") + + *Input:* prices, period \ + *Output:* ema + + ema \<\- [prices[0]] \ + multiplier \<\- 2 / (period + 1) + + *for* price *in* prices[1:] \ + #h(20pt)*if* price < ema[-1] \ + #h(40pt)ema.append(price) \ + #h(20pt)*else* \ + #h(40pt)new_ema \<\- (price - ema[-1]) \* multiplier + ema[-1] \ + #h(40pt)ema.append(new_ema) \ + #h(20pt)*end if* \ + *end for* + + *return* ema +], caption: "Simple EMA Algorithm") + +== Notes +1. The algorithm initializes the EMA with the first price in the series. +2. For each subsequent price: + - If the new price is lower than the previous EMA, it immediately becomes the new EMA value. + - If the new price is higher, a standard EMA calculation is applied. +3. This approach ensures quick adaptation to price drops while smoothing out price increases. + +== Reference Implementation +For a reference implementation, refer to the `calculate_simple_ema` function in the `oracles.py` file. diff --git a/version.txt b/version.txt new file mode 100644 index 0000000..4f31683 --- /dev/null +++ b/version.txt @@ -0,0 +1 @@ +DEV VERSION \ No newline at end of file